static public <T> void read(byte[] buf, Header hdr, Decoder<T> decoder, Consumer<T> consumer) {
        if (hdr == null) {
            hdr = header(buf);
        }
        final int str0 = hdr.row0 + hdr.rowSize * hdr.rowCount;
        StringReader r = (i) -> {
            int pos = str0 + i;
            if (pos < str0 || pos >= buf.length) {
                throw new Error("String index outside of buffer: " + i, null);
            }
            int end = pos;
            while (buf[end] != 0) {
                end++;
            }
            if (pos == end) {
                return null;
            }
            return new String(buf, pos, end - pos, StandardCharsets.UTF_8);
        };
        ByteBuffer bb = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN);
        try {            
            for (int i = 0; i < hdr.rowCount; i++) {     
                int pos = hdr.row0 + i * hdr.rowSize;
                bb.limit(pos + hdr.rowSize);
                bb.position(pos);
                consumer.accept(decoder.decode(bb, r));
            }
        } catch (Error err) {
            throw err;
        } catch (RuntimeException err) {
            throw new Error("Error parsing rows", err);
        }
    }