Skip to content

Commit 9796afb

Browse files
committed
feat: add ZarrUnicodeStringDataCodec
1 parent 3955cdf commit 9796afb

1 file changed

Lines changed: 67 additions & 0 deletions

File tree

src/main/java/org/janelia/saalfeldlab/n5/codec/DataCodec.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)