Skip to content

Commit 72e0a25

Browse files
committed
fix!: ZarrFixedLengthStringDataCodec length encoding
* truncate too-long strings during encoding * do not compute max length during encoding * rename static methods to get String DataCodecs * add doc
1 parent 9796afb commit 72e0a25

1 file changed

Lines changed: 53 additions & 12 deletions

File tree

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

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import java.util.Arrays;
3939
import java.util.function.IntFunction;
4040
import org.janelia.saalfeldlab.n5.DataBlock;
41-
import org.janelia.saalfeldlab.n5.N5Exception;
4241
import org.janelia.saalfeldlab.n5.N5Exception.N5IOException;
4342
import 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

Comments
 (0)