Repository: wangguanquan/eec (java)

Issue:
单元格长度限制
当单元格里的字符串长度超过2048时，报堆内存不足错误
java.nio.BufferOverflowException
	at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:189)
	at java.nio.ByteBuffer.put(ByteBuffer.java:859)
	at org.ttzero.excel.entity.SharedStringTable.push(SharedStringTable.java:145)
	at org.ttzero.excel.entity.SharedStrings.add(SharedStrings.java:216)
	at org.ttzero.excel.entity.SharedStrings.get(SharedStrings.java:182)

Pull request description:
Fix #161 单元格值长度超过2048时抛BufferOverflowException

Patch:
diff --git a/pom.xml b/pom.xml
index fd62c81a..c905da45 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
 
   <groupId>org.ttzero</groupId>
   <artifactId>eec</artifactId>
-  <version>0.4.9</version>
+  <version>0.4.10-SNAPSHOT</version>
   <name>Excel Write/Read Framework</name>
   <packaging>jar</packaging>
   <url>https://www.ttzero.org</url>
diff --git a/src/main/java/org/ttzero/excel/entity/SharedStringTable.java b/src/main/java/org/ttzero/excel/entity/SharedStringTable.java
index 38b0aed3..f21c41fa 100644
--- a/src/main/java/org/ttzero/excel/entity/SharedStringTable.java
+++ b/src/main/java/org/ttzero/excel/entity/SharedStringTable.java
@@ -64,6 +64,11 @@ public class SharedStringTable implements Closeable, Iterable<String> {
      */
     protected boolean shouldDelete;
 
+    /**
+     * Buffer size(4K)
+     */
+    protected int defaultBufferSize = 1 << 12;
+
     /**
      * Create a temp file to storage shared strings
      *
@@ -73,7 +78,7 @@ public class SharedStringTable implements Closeable, Iterable<String> {
         temp = Files.createTempFile("+", ".sst");
         shouldDelete = true;
         channel = Files.newByteChannel(temp, StandardOpenOption.WRITE, StandardOpenOption.READ);
-        buffer = ByteBuffer.allocate(1 << 11);
+        buffer = ByteBuffer.allocate(defaultBufferSize);
         buffer.order(ByteOrder.LITTLE_ENDIAN);
         // Total keyword storage the header 4 bytes
         buffer.putInt(0);
@@ -93,7 +98,7 @@ public class SharedStringTable implements Closeable, Iterable<String> {
         this.temp = path;
         channel = Files.newByteChannel(temp, StandardOpenOption.WRITE, StandardOpenOption.READ);
 
-        buffer = ByteBuffer.allocate(1 << 11);
+        buffer = ByteBuffer.allocate(defaultBufferSize);
         buffer.order(ByteOrder.LITTLE_ENDIAN);
 
         channel.read(buffer);
@@ -138,6 +143,10 @@ public class SharedStringTable implements Closeable, Iterable<String> {
             return pushChar(key.charAt(0));
         }
         byte[] bytes = key.getBytes(UTF_8);
+        // The byte length exceeds 4k
+        if (bytes.length + 4 > defaultBufferSize) {
+           return -1;
+        }
         if (buffer.remaining() < bytes.length + 4) {
             flush();
         }
diff --git a/src/main/java/org/ttzero/excel/entity/SharedStrings.java b/src/main/java/org/ttzero/excel/entity/SharedStrings.java
index c4d333f4..0268e29f 100644
--- a/src/main/java/org/ttzero/excel/entity/SharedStrings.java
+++ b/src/main/java/org/ttzero/excel/entity/SharedStrings.java
@@ -208,12 +208,15 @@ public class SharedStrings implements Storable, Closeable {
         // Convert to inline string when the cache full
 //        if (sst.size() > expectedInsertions) return -1;
 
-        writer.write("<si><t>");
-        writer.escapeWrite(key);
-        writer.write("</t></si>");
-
         // Add to table
-        return sst.push(key);
+        int n = sst.push(key);
+        // Ignore write into SST
+        if (n >= 0) {
+            writer.write("<si><t>");
+            writer.escapeWrite(key);
+            writer.write("</t></si>");
+        }
+        return n;
     }
 
     private int add(char c) throws IOException {


Test patch:
diff --git a/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java b/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java
index 30de1e32..3cf56140 100644
--- a/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java
@@ -26,6 +26,7 @@ import java.sql.Timestamp;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.ZoneOffset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -33,6 +34,7 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 /**
  * @author guanquan.wang at 2019-04-28 19:16
@@ -262,6 +264,37 @@ public class ListMapSheetTest extends WorkbookTest {
         }).writeTo(defaultTestPath);
     }
 
+    @Test public void test_161() throws IOException {
+        new Workbook(("Issue#161")).addSheet(new ListMapSheet() {
+            private int i = 0;
+            @Override
+            protected List<Map<String, ?>> more() {
+                // Only write one row
+                if (i++ > 0) return null;
+                List<Map<String, ?>> list = new ArrayList<>();
+                Map<String, Object> map = new HashMap<>();
+                map.put("a0172da4c398047aeac758ecd4a799b71", UUID.randomUUID().toString());
+                map.put("hobbies", new ArrayList<String>() {{
+                    add("张");
+                    add("李");
+                }});
+                map.put("sex", "男");
+                final int len = 4095;
+                StringBuilder buf = new StringBuilder(len);
+                for (int i = 0; i < len; i++) {
+                    buf.append('a');
+                }
+                // java.nio.BufferOverflowException occur when the cell value length large than 2045
+                map.put("name", buf.toString());
+                map.put("age", 24);
+                map.put("createDate", new Date(1535444725000L).toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime());
+
+                list.add(map);
+                return list;
+            }
+        }).writeTo(defaultTestPath);
+    }
+
     public static List<Map<String, ?>> createTestData() {
         int size = random.nextInt(100) + 1;
         return createTestData(size);