@@ -85,6 +85,7 @@ public T createData(final int numElements) {
8585
8686 public static final DataCodec <String []> STRING = new N5StringDataCodec ();
8787 public static final DataCodec <String []> ZARR_STRING = new ZarrStringDataCodec ();
88+
8889 public static final DataCodec <byte []> OBJECT = new ObjectDataCodec ();
8990
9091 public static DataCodec <short []> SHORT (ByteOrder order ) {
@@ -107,6 +108,10 @@ public static DataCodec<double[]> DOUBLE(ByteOrder order) {
107108 return order == ByteOrder .BIG_ENDIAN ? DOUBLE_BIG_ENDIAN : DOUBLE_LITTLE_ENDIAN ;
108109 }
109110
111+ public static DataCodec <String []> ZARR_UNICODE (int nChars , ByteOrder order ) {
112+ return new ZarrUnicodeStringDataCodec (nChars , order );
113+ }
114+
110115 // ---------------- implementations -----------------
111116 //
112117
@@ -334,6 +339,68 @@ public String[] deserialize(ReadData readData, int numElements) throws N5IOExcep
334339 }
335340 }
336341
342+ private static final class ZarrUnicodeStringDataCodec extends DataCodec <String []> {
343+
344+ private static final char NULL_CHAR = '\0' ;
345+ private static final int BYTES_PER_CHAR = 4 ;
346+
347+ private final Charset charset ;
348+
349+ ZarrUnicodeStringDataCodec (int nChar , ByteOrder order ) {
350+ super (nChar * BYTES_PER_CHAR , String []::new );
351+ if (order == ByteOrder .BIG_ENDIAN )
352+ charset = Charset .forName ("UTF-32BE" );
353+ else
354+ charset = Charset .forName ("UTF-32LE" );
355+ }
356+
357+ @ Override
358+ public ReadData serialize (String [] data ) throws N5IOException {
359+
360+ if ( data .length == 0 )
361+ return ReadData .empty ();
362+
363+ 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 ;
367+ final ByteBuffer buf = ByteBuffer .allocate (totalSize );
368+ int pos = 0 ;
369+ for ( int i = 0 ; i < N ; i ++ ) {
370+ buf .position (pos );
371+ buf .put (charset .encode (data [i ]));
372+ pos += fixedEncodedStringSize ;
373+ }
374+ return ReadData .from (buf .array ());
375+ }
376+
377+ @ Override
378+ public String [] deserialize (ReadData readData , int numElements ) throws N5IOException {
379+
380+ if (readData .length () % numElements != 0 ) {
381+ throw new RuntimeException (String .format ("Data of length (%d) bytes is not a multiple of numElements (%d)" ,
382+ readData .length (), numElements ));
383+ }
384+
385+ final int bytesPerString = (int )readData .length () / numElements ;
386+ final ByteBuffer serialized = readData .toByteBuffer ();
387+ int pos = 0 ;
388+ final String [] out = new String [numElements ];
389+ for (int i = 0 ; i < numElements ; i ++) {
390+ serialized .position (pos );
391+ serialized .limit (pos + bytesPerString );
392+ out [i ] = removeTrailingNullChars (charset .decode (serialized ).toString ());
393+ pos += bytesPerString ;
394+ }
395+ return out ;
396+ }
397+
398+ private static String removeTrailingNullChars (String str ) {
399+ int idx = str .indexOf (NULL_CHAR );
400+ return idx < 0 ? str : str .substring (0 , idx );
401+ }
402+ }
403+
337404 private static final class ObjectDataCodec extends DataCodec <byte []> {
338405
339406 ObjectDataCodec () {
0 commit comments