3838import java .util .Arrays ;
3939import java .util .function .IntFunction ;
4040import org .janelia .saalfeldlab .n5 .DataBlock ;
41- import org .janelia .saalfeldlab .n5 .N5Exception ;
4241import org .janelia .saalfeldlab .n5 .N5Exception .N5IOException ;
4342import org .janelia .saalfeldlab .n5 .readdata .ReadData ;
4443
@@ -84,7 +83,7 @@ public T createData(final int numElements) {
8483 public static final DataCodec <double []> DOUBLE_LITTLE_ENDIAN = new DoubleDataCodec (ByteOrder .LITTLE_ENDIAN );
8584
8685 public static final DataCodec <String []> STRING = new N5StringDataCodec ();
87- public static final DataCodec <String []> ZARR_STRING = new ZarrStringDataCodec ();
86+ public static final DataCodec <String []> ZARR_VAR_STRING = new ZarrStringDataCodec ();
8887
8988 public static final DataCodec <byte []> OBJECT = new ObjectDataCodec ();
9089
@@ -108,8 +107,22 @@ public static DataCodec<double[]> DOUBLE(ByteOrder order) {
108107 return order == ByteOrder .BIG_ENDIAN ? DOUBLE_BIG_ENDIAN : DOUBLE_LITTLE_ENDIAN ;
109108 }
110109
111- public static DataCodec <String []> ZARR_UNICODE (int nChars , ByteOrder order ) {
112- return new ZarrUnicodeStringDataCodec (nChars , order );
110+ /**
111+ * Returns a {@link DataCodec} for fixed-length strings.
112+ * <p>
113+ * The resulting codec uses UTF-32 string encoding to encode / decode
114+ * String arrays to a pre-defined fixed-length. Shorter (longer)
115+ * Strings will be padded (truncated) as necessary.
116+ *
117+ * @param maxLength
118+ * the maximum number of characters per string
119+ * @param order
120+ * the {@link ByteOrder} for encoding
121+ * @return
122+ * a data codec for fixed-length strings
123+ */
124+ public static DataCodec <String []> ZARR_FIXED_STRING (int maxLength , ByteOrder order ) {
125+ return new ZarrFixedLengthStringDataCodec (maxLength , order );
113126 }
114127
115128 // ---------------- implementations -----------------
@@ -291,6 +304,22 @@ public String[] deserialize(ReadData readData, int numElements) throws N5IOExcep
291304 }
292305 }
293306
307+ /**
308+ * A {@link DataCodec} for variable-length strings.
309+ * <p>
310+ * Uses UTF-8 string encoding to encode / decode String arrays, the format
311+ * is:
312+ * <ul>
313+ * <li>The total number of Strings is encoded as an int.</li>
314+ * <li>For each String</li>
315+ * <ul>
316+ * <li>The number of characters in the string is encoded as an int</li>
317+ * <li>The String is encoded using UTF-8.</li>
318+ * </ul>
319+ * </ul>
320+ *
321+ * @see {{@link #ZARR_FIXED_STRING(int, ByteOrder)}
322+ */
294323 private static final class ZarrStringDataCodec extends DataCodec <String []> {
295324
296325 private static final Charset ENCODING = StandardCharsets .UTF_8 ;
@@ -339,15 +368,27 @@ public String[] deserialize(ReadData readData, int numElements) throws N5IOExcep
339368 }
340369 }
341370
342- private static final class ZarrUnicodeStringDataCodec extends DataCodec <String []> {
371+ /**
372+ * A {@link DataCodec} for fixed-length strings.
373+ * <p>
374+ * Uses UTF-32 string encoding to encode / decode String arrays to a
375+ * pre-defined fixed-length. Shorter (longer) Strings will be padded
376+ * (truncated) as necessary.
377+ *
378+ * @see {{@link #ZARR_FIXED_STRING(int, ByteOrder)}
379+ */
380+ private static final class ZarrFixedLengthStringDataCodec extends DataCodec <String []> {
343381
344382 private static final char NULL_CHAR = '\0' ;
345383 private static final int BYTES_PER_CHAR = 4 ;
346384
347385 private final Charset charset ;
348386
349- ZarrUnicodeStringDataCodec (int nChar , ByteOrder order ) {
350- super (nChar * BYTES_PER_CHAR , String []::new );
387+ private final int maxLength ;
388+
389+ ZarrFixedLengthStringDataCodec (int maxLength , ByteOrder order ) {
390+ super (maxLength * BYTES_PER_CHAR , String []::new );
391+ this .maxLength = maxLength ;
351392 if (order == ByteOrder .BIG_ENDIAN )
352393 charset = Charset .forName ("UTF-32BE" );
353394 else
@@ -361,15 +402,15 @@ public ReadData serialize(String[] data) throws N5IOException {
361402 return ReadData .empty ();
362403
363404 final int N = data .length ;
364- final int maxLength = Arrays .stream (data ).mapToInt ( String ::length ).max ().getAsInt ();
365- final int fixedEncodedStringSize = maxLength * BYTES_PER_CHAR ;
366- final int totalSize = N * fixedEncodedStringSize ;
405+ final int encodedStringSize = maxLength * BYTES_PER_CHAR ;
406+ final int totalSize = N * encodedStringSize ;
367407 final ByteBuffer buf = ByteBuffer .allocate (totalSize );
368408 int pos = 0 ;
369409 for ( int i = 0 ; i < N ; i ++ ) {
370410 buf .position (pos );
371- buf .put (charset .encode (data [i ]));
372- pos += fixedEncodedStringSize ;
411+ final String s = data [i ].length () > maxLength ? data [i ].substring (0 , maxLength ) : data [i ];
412+ buf .put (charset .encode (s ));
413+ pos += encodedStringSize ;
373414 }
374415 return ReadData .from (buf .array ());
375416 }
0 commit comments