• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gephi / graphstore / #570

17 Aug 2026 06:29PM UTC coverage: 91.198% (-0.04%) from 91.242%
#570

push

web-flow
Harden serialization: fail on unknown tags, fix char encoding (#284)

* Harden serialization: fail on unknown tags, fix char encoding

Three scoped fixes to the serialization layer. No change to the on-disk
format, so no VERSION bump.

- deserialize() silently returned null for an unrecognized type tag,
  surfacing later as a confusing ClassCastException or silent data loss.
  It now throws IOException naming the tag. The preceding `case -1` was
  unreachable (readUnsignedByte never returns -1) and is removed.

- CHAR and CHAR_ARRAY relied on DataOutput.writeChar/readChar, but
  DataInputOutput implements those with 4 bytes instead of the 2 the
  interface specifies. Production writes via DataOutputStream, so no
  stored data is affected, but graphstore's own tests were round-tripping
  an encoding that never reaches disk. Both sides now use
  writeShort/readUnsignedShort, which is byte-identical to
  DataOutputStream.writeChar, and DataInputOutput.writeChar/readChar are
  fixed to honour the contract.

- Dropped Locale support. Locale is not an AttributeUtils supported type,
  so it cannot enter a graph through the public API. Tag 124 is kept
  reserved so it is never reused.

Adds a test asserting all serialization tag constants are distinct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Cover non-ASCII chars in serialization tests

The char tests only used ASCII values, so they could not detect a
narrowing of the 2-byte encoding. Extend them across the boundaries of
the 16-bit range: above 0x7F, above 0x7FF, either side of the
signed-short flip, the 16-bit maximum, and an unpaired surrogate.

Verified by temporarily narrowing CHAR to a symmetric 1-byte encoding,
which the previous values did not catch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

4 of 7 new or added lines in 2 files covered. (57.14%)

4 existing lines in 1 file now uncovered.

11791 of 12929 relevant lines covered (91.2%)

0.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.57
/src/main/java/org/gephi/graph/impl/utils/DataInputOutput.java
1
/*
2
 * Copyright 2012-2013 Gephi Consortium
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
 * use this file except in compliance with the License. You may obtain a copy of
6
 * the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 * License for the specific language governing permissions and limitations under
14
 * the License.
15
 */
16
package org.gephi.graph.impl.utils;
17

18
import java.io.DataInput;
19
import java.io.DataOutput;
20
import java.io.IOException;
21
import java.io.ObjectInput;
22
import java.io.ObjectOutput;
23
import java.nio.ByteBuffer;
24
import java.util.Arrays;
25
import org.gephi.graph.impl.Serialization;
26

27
/**
28
 * Input/Output utility.
29
 */
30
public final class DataInputOutput implements DataInput, DataOutput, ObjectInput, ObjectOutput {
31

32
    private int pos = 0;
1✔
33
    private int count = 0;
1✔
34
    private byte[] buf;
35

36
    public DataInputOutput() {
1✔
37
        buf = new byte[8];
1✔
38
    }
1✔
39

40
    public DataInputOutput(byte[] data) {
1✔
41
        buf = data;
1✔
42
        count = data.length;
1✔
43
    }
1✔
44

45
    public byte[] getBuf() {
46
        return buf;
1✔
47
    }
48

49
    public int getPos() {
50
        return pos;
×
51
    }
52

53
    public DataInputOutput reset() {
54
        pos = 0;
1✔
55
        count = 0;
1✔
56
        return this;
1✔
57
    }
58

59
    public void resetForReading() {
60
        count = pos;
×
61
        pos = 0;
×
62
    }
×
63

64
    public DataInputOutput reset(byte[] b) {
65
        pos = 0;
1✔
66
        buf = b;
1✔
67
        count = b.length;
1✔
68
        return this;
1✔
69
    }
70

71
    public byte[] toByteArray() {
72
        byte[] d = new byte[pos];
1✔
73
        System.arraycopy(buf, 0, d, 0, pos);
1✔
74
        return d;
1✔
75
    }
76

77
    @Override
78
    public int available() {
79
        return count - pos;
1✔
80
    }
81

82
    @Override
83
    public void readFully(byte[] b) throws IOException {
84
        readFully(b, 0, b.length);
1✔
85
    }
1✔
86

87
    @Override
88
    public void readFully(byte[] b, int off, int len) throws IOException {
89
        System.arraycopy(buf, pos, b, off, len);
1✔
90
        pos += len;
1✔
91
    }
1✔
92

93
    @Override
94
    public int skipBytes(int n) throws IOException {
95
        pos += n;
×
96
        return n;
×
97
    }
98

99
    @Override
100
    public boolean readBoolean() throws IOException {
101
        return buf[pos++] == 1;
1✔
102
    }
103

104
    @Override
105
    public byte readByte() throws IOException {
106
        return buf[pos++];
1✔
107
    }
108

109
    @Override
110
    public int readUnsignedByte() throws IOException {
111
        return buf[pos++] & 0xff;
1✔
112
    }
113

114
    @Override
115
    public short readShort() throws IOException {
116
        return (short) (((short) (buf[pos++] & 0xff) << 8) | ((short) (buf[pos++] & 0xff) << 0));
1✔
117

118
    }
119

120
    @Override
121
    public int readUnsignedShort() throws IOException {
122
        return (((int) (buf[pos++] & 0xff) << 8) | ((int) (buf[pos++] & 0xff) << 0));
1✔
123
    }
124

125
    @Override
126
    public char readChar() throws IOException {
NEW
127
        return (char) readUnsignedShort();
×
128
    }
129

130
    @Override
131
    public int readInt() throws IOException {
132
        return (((buf[pos++] & 0xff) << 24) | ((buf[pos++] & 0xff) << 16) | ((buf[pos++] & 0xff) << 8) | ((buf[pos++] & 0xff) << 0));
1✔
133

134
    }
135

136
    @Override
137
    public long readLong() throws IOException {
138
        return (((long) (buf[pos++] & 0xff) << 56) | ((long) (buf[pos++] & 0xff) << 48) | ((long) (buf[pos++] & 0xff) << 40) | ((long) (buf[pos++] & 0xff) << 32) | ((long) (buf[pos++] & 0xff) << 24) | ((long) (buf[pos++] & 0xff) << 16) | ((long) (buf[pos++] & 0xff) << 8) | ((long) (buf[pos++] & 0xff) << 0));
1✔
139

140
    }
141

142
    @Override
143
    public float readFloat() throws IOException {
144
        return Float.intBitsToFloat(readInt());
1✔
145
    }
146

147
    @Override
148
    public double readDouble() throws IOException {
149
        return Double.longBitsToDouble(readLong());
1✔
150
    }
151

152
    @Override
153
    public String readLine() throws IOException {
154
        return readUTF();
×
155
    }
156

157
    @Override
158
    public String readUTF() throws IOException {
UNCOV
159
        return Serialization.deserializeString(this);
×
160
    }
161

162
    /**
163
     * make sure there will be enought space in buffer to write N bytes
164
     */
165
    private void ensureAvail(int n) {
166
        if (pos + n >= buf.length) {
1✔
167
            int newSize = Math.max(pos + n, buf.length * 2);
1✔
168
            buf = Arrays.copyOf(buf, newSize);
1✔
169
        }
170
    }
1✔
171

172
    @Override
173
    public void write(int b) throws IOException {
174
        ensureAvail(1);
1✔
175
        buf[pos++] = (byte) b;
1✔
176
    }
1✔
177

178
    @Override
179
    public void write(byte[] b) throws IOException {
180
        write(b, 0, b.length);
1✔
181
    }
1✔
182

183
    @Override
184
    public void write(byte[] b, int off, int len) throws IOException {
185
        ensureAvail(len);
1✔
186
        System.arraycopy(b, off, buf, pos, len);
1✔
187
        pos += len;
1✔
188
    }
1✔
189

190
    @Override
191
    public void writeBoolean(boolean v) throws IOException {
192
        ensureAvail(1);
1✔
193
        buf[pos++] = (byte) (v ? 1 : 0);
1✔
194
    }
1✔
195

196
    @Override
197
    public void writeByte(int v) throws IOException {
198
        ensureAvail(1);
1✔
199
        buf[pos++] = (byte) (v);
1✔
200
    }
1✔
201

202
    @Override
203
    public void writeShort(int v) throws IOException {
204
        ensureAvail(2);
1✔
205
        buf[pos++] = (byte) (0xff & (v >> 8));
1✔
206
        buf[pos++] = (byte) (0xff & (v >> 0));
1✔
207

208
    }
1✔
209

210
    @Override
211
    public void writeChar(int v) throws IOException {
NEW
212
        writeShort(v);
×
UNCOV
213
    }
×
214

215
    @Override
216
    public void writeInt(int v) throws IOException {
217
        ensureAvail(4);
1✔
218
        buf[pos++] = (byte) (0xff & (v >> 24));
1✔
219
        buf[pos++] = (byte) (0xff & (v >> 16));
1✔
220
        buf[pos++] = (byte) (0xff & (v >> 8));
1✔
221
        buf[pos++] = (byte) (0xff & (v >> 0));
1✔
222

223
    }
1✔
224

225
    @Override
226
    public void writeLong(long v) throws IOException {
227
        ensureAvail(8);
1✔
228
        buf[pos++] = (byte) (0xff & (v >> 56));
1✔
229
        buf[pos++] = (byte) (0xff & (v >> 48));
1✔
230
        buf[pos++] = (byte) (0xff & (v >> 40));
1✔
231
        buf[pos++] = (byte) (0xff & (v >> 32));
1✔
232
        buf[pos++] = (byte) (0xff & (v >> 24));
1✔
233
        buf[pos++] = (byte) (0xff & (v >> 16));
1✔
234
        buf[pos++] = (byte) (0xff & (v >> 8));
1✔
235
        buf[pos++] = (byte) (0xff & (v >> 0));
1✔
236
    }
1✔
237

238
    @Override
239
    public void writeFloat(float v) throws IOException {
240
        ensureAvail(4);
1✔
241
        writeInt(Float.floatToIntBits(v));
1✔
242
    }
1✔
243

244
    @Override
245
    public void writeDouble(double v) throws IOException {
246
        ensureAvail(8);
1✔
247
        writeLong(Double.doubleToLongBits(v));
1✔
248
    }
1✔
249

250
    @Override
251
    public void writeBytes(String s) throws IOException {
252
        writeUTF(s);
×
253
    }
×
254

255
    @Override
256
    public void writeChars(String s) throws IOException {
257
        writeUTF(s);
×
258
    }
×
259

260
    @Override
261
    public void writeUTF(String s) throws IOException {
UNCOV
262
        Serialization.serializeString(this, s);
×
UNCOV
263
    }
×
264

265
    public void writeFromByteBuffer(ByteBuffer b, int offset, int length) {
266
        ensureAvail(length);
×
267
        b.position(offset);
×
268
        b.get(buf, pos, length);
×
269
        pos += length;
×
270
    }
×
271

272
    @Override
273
    public int read() throws IOException {
274
        // is here just to implement ObjectInput
275
        return readUnsignedByte();
×
276
    }
277

278
    @Override
279
    public int read(byte[] b) throws IOException {
280
        // is here just to implement ObjectInput
281
        readFully(b);
×
282
        return b.length;
×
283
    }
284

285
    @Override
286
    public int read(byte[] b, int off, int len) throws IOException {
287
        // is here just to implement ObjectInput
288
        readFully(b, off, len);
×
289
        return len;
×
290
    }
291

292
    @Override
293
    public long skip(long n) throws IOException {
294
        // is here just to implement ObjectInput
295
        pos += n;
1✔
296
        return n;
1✔
297
    }
298

299
    @Override
300
    public void close() throws IOException {
301
        // is here just to implement ObjectInput
302
        // do nothing
303
    }
×
304

305
    @Override
306
    public void flush() throws IOException {
307
        // is here just to implement ObjectOutput
308
        // do nothing
309
    }
×
310

311
    @Override
312
    public Object readObject() throws ClassNotFoundException, IOException {
313
        throw new UnsupportedOperationException("Not supported");
×
314
    }
315

316
    @Override
317
    public void writeObject(Object o) throws IOException {
318
        throw new UnsupportedOperationException("Not supported");
×
319
    }
320
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc