Rev

Rev 1822 | Details | Compare with Previous | Last modification | View Log | SVN | Bug Tracker

Rev Author Line No. Line
1133 berge 1
/**
2
 * @author      ETSI / STF462 / Alexandre Berge
3
 * @version     $URL: file:///D:/RepositoriesNew/ITS/trunk/javasrc/codec/org/etsi/ttcn/codec/CodecBuffer.java $
4
 *              $Id: CodecBuffer.java 2655 2017-01-26 10:46:08Z filatov $
5
 */
6
package org.etsi.ttcn.codec;
7
 
8
import java.util.Map;
9
import java.util.TreeMap;
10
 
2655 filatov 11
import org.etsi.adapter.TERFactory;
12
import org.etsi.common.ByteHelper;
1133 berge 13
 
14
public class CodecBuffer {
15
 
16
    /**
17
     * Raw byte storage array. Last byte may be a "partial byte", i.e. some of its bits may not be significants
18
     * @see     bits
19
     */
20
    protected byte[] buffer;
21
 
22
    /**
23
     * Number of significant bits in the "partial byte"
24
     * Significant bits are always stored "left-aligned" (i.e. MSBs) in the "partial byte"
25
     * @see     buffer
26
     */
27
    protected int bits;
28
 
29
    /**
30
     * Marker storage
31
     */
32
    protected Map<String, Marker> markers = new TreeMap<String, Marker>();
33
 
34
    /**
1143 berge 35
     * Some useful byte masks
1133 berge 36
     */
37
    private byte[] masks = new byte[]{(byte)0x00, (byte)0x80, (byte)0xC0, (byte)0xE0, (byte)0xF0, (byte)0xF8, (byte)0xFC, (byte)0xFE};
38
 
39
    /**
1143 berge 40
     * Some useful byte masks
1133 berge 41
     */
42
    private byte[] nomasks = new byte[]{(byte)0xFF, (byte)0x7F, (byte)0x3F, (byte)0x1F, (byte)0x0F, (byte)0x07, (byte)0x03, (byte)0x01};
43
 
44
    /**
45
     * Main constructor. Initialises an empty buffer
46
     */
47
    public CodecBuffer() {
48
        bits = 0;
49
        buffer = new byte[]{};
50
    }
51
 
52
    /**
53
     * Constructor. Initialises the buffer using provided byte array
54
     * @param bytes     Initial content of the buffer
55
     */
56
    public CodecBuffer(byte[] bytes) {
57
        bits = 0;
58
        buffer = bytes.clone();
59
    }
60
 
61
    /**
1173 berge 62
     * Overwrite content of current buffer using data of newBuffer
63
     * @param newBuffer CodecBuffer containing new data
64
     */
65
    public void overwriteWith(CodecBuffer newBuffer) {
66
 
67
        bits = newBuffer.bits;
68
        buffer = newBuffer.buffer.clone();
69
        markers = new TreeMap<String, Marker>(newBuffer.markers);      
70
    }
71
 
72
    /**
1133 berge 73
     * Retrieves the number of significant bits in the buffer.
74
     * Warning: getNbBytes() != (getNbBits() * 8)
75
     * @return  The number of significant bits in the buffer
76
     * @see     getNbBytes()
77
     */
78
    public int getNbBits() {
79
 
80
        if(bits > 0) {
81
            return (buffer.length - 1) * 8 + bits;
82
        }
83
        return buffer.length * 8;
84
    }
85
 
86
    /**
87
     * Retrieves the number of bytes used to store the buffer.
88
     * Warning: getNbBytes() != (getNbBits() * 8)
89
     * @return  The number of bytes used to store the buffer
90
     * @see     getNbBits()
91
     */
92
    public int getNbBytes() {
93
        return buffer.length;
94
    }
95
 
96
    /**
97
     * Concatenates current CodecBuffer and the CodecBuffer passed as parameter.
98
     * Markers of buffer2 are preserved and integrated into the current CodecBuffer.
99
     * @param buffer2   The CodecBuffer to be appended
100
     */
101
    public void append(CodecBuffer buffer2) {
102
 
103
        // copy buffer content
104
        int nbBits = getNbBits();
105
        if(buffer2.getNbBits() > 0) {
106
            appendBits(buffer2.getBits(), buffer2.getNbBits());
107
        }
108
 
109
        // integrate markers
110
        for(Map.Entry<String, Marker> entry : buffer2.markers.entrySet()) {
111
            String key = entry.getKey();
112
            Marker marker = entry.getValue();
113
            marker.move(nbBits);
114
            markers.put(key, marker);
115
        }
116
    }
117
 
118
    /**
119
     * Associates a new marker to current CodecBuffer.
120
     * Inserting marker with name identical to a previously inserted one will overwrite it.
121
     * @param key       Name of the marker
122
     * @param pos       Position of the marker relative to the current position
123
     * @param callback  Optional callback object to be executed later, or null
124
     * @see             runCallbacks()
125
     */
126
    public void setMarker(String key, int pos, IMarkerCallback callback) {
127
        markers.put(key, new Marker(pos, callback));
128
    }
129
 
130
    /**
131
     * Executes all the callbacks associated to current CodecBuffer's markers
132
     * @see     setMarkers()
133
     */
134
    public void runCallbacks() {
2655 filatov 135
//        TERFactory.getInstance().logDebug("Running callbacks...");
1133 berge 136
        for(Map.Entry<String, Marker> entry : markers.entrySet()) {
137
            String key = entry.getKey();
2655 filatov 138
//            TERFactory.getInstance().logDebug("Running callback: " + key);
1133 berge 139
            Marker marker = entry.getValue();
140
            IMarkerCallback callback = marker.getCallback();
141
            if(callback != null) {
142
                CodecBuffer Left = getBuffer(0, marker.getPos());
143
                CodecBuffer Right = getBuffer(marker.getPos(), getNbBits() - marker.getPos());
144
 
145
                callback.run(key, Left, Right);
146
 
147
                // Overwrite self with Left+Right
148
                // TODO: take care if Right or Left have change to much.
149
                Left.append(Right);
150
                buffer = Left.buffer;
151
                bits = Left.bits;
152
            }
153
        }      
154
    }
155
 
156
    /**
157
     * Creates a new CodecBuffer from current CodecBuffer's content
158
     * Markers of current CodecBuffer are preserved and integrated into the new CodecBuffer.
159
     * @param start     Start point. Bit offset from current position
160
     * @param length    Amount of bits to be copied
161
     * @return          The new CodecBuffer
162
     */
163
    public CodecBuffer getBuffer(int start, int length) {
164
        // TODO: check param validity
165
 
166
        CodecBuffer res = new CodecBuffer();
167
        res.setBits(getBits(start, length), length);
168
 
169
        for(Map.Entry<String, Marker> entry : markers.entrySet()) {
170
            String key = entry.getKey();
171
            Marker marker = entry.getValue();
172
            int pos = marker.getPos();
173
            if(pos >= start && pos < (start + length)) {
174
                res.setMarker(key, pos - start, marker.getCallback());
175
            }
176
        }      
177
        return res;
178
    }
179
 
180
    /**
181
     * Appends some raw bytes at the end of the current CodecBuffer
182
     * @param origBytes     Bytes to be appended
183
     */
184
    public void appendBytes(byte[] origBytes) {
185
 
186
        byte[] bytes = origBytes.clone();
187
 
188
        int lastByte = buffer.length - 1;
189
        buffer = ByteHelper.concat(buffer, bytes);
190
 
191
        if(bits != 0) {
192
            for(int i=0; i < bytes.length; i++) {
193
                buffer[lastByte] &= masks[bits];
194
                buffer[lastByte] |= ((bytes[i] >>> bits) & nomasks[bits]);
195
                lastByte++;
196
                buffer[lastByte] = (byte)(buffer[lastByte] << (8 - bits));
197
            }
198
 
199
            buffer[buffer.length - 1] &= masks[bits];
200
        }
201
    }
202
 
203
    /**
204
     * Appends some raw bits at the end of the current CodecBuffer
205
     * @param origBytes     Byte array used to store the bits to be appended.  
206
     *                      It MUST be right-aligned. First byte (origBytes[0]) may be a
207
     *                      partial byte if 'nbBits' is not a multiple of 8.
208
     *                      In this case MSBs of this byte are ignored and not copied
209
     * @param nbBits        Number of significant bits in 'origBytes'
210
     */
211
    public void appendBits(byte[] origBytes, int nbBits) {
212
 
213
        byte[] bytes = origBytes.clone();
214
        int rbits = nbBits % 8;
215
        int nbBytes = nbBits / 8 + ((rbits > 0)?1:0);
216
        int lastByte = buffer.length - 1;
217
 
218
        // Left-align received bytes
219
        if(rbits !=0) {
220
            int i;
221
            for(i=(bytes.length - nbBytes); i < (nbBytes - 1); i++) {
222
                bytes[i] = (byte)(bytes[i] << (8 - rbits));
223
                bytes[i] |= ((bytes[i+1] >>> rbits) & nomasks[rbits]);
224
            }
225
            bytes[i] = (byte)(bytes[i] << (8 - rbits));
226
            bytes[i] &= masks[rbits];
227
        }
228
 
229
        buffer = ByteHelper.concat(buffer, ByteHelper.extract(bytes, (bytes.length - nbBytes), nbBytes));
230
        if(bits != 0) {
231
            int i;
232
            for(i=lastByte; i < (lastByte + nbBytes); i++) {
233
                buffer[i] &= masks[bits];
234
                buffer[i] |= ((buffer[i+1] >>> bits) & nomasks[bits]);
235
                buffer[i+1] = (byte)(buffer[i+1] << (8 - bits));
236
            }
237
            buffer[i] &= masks[bits];
238
 
239
            if((rbits > 0) && (rbits + bits <= 8)) {
240
                // Remove trailing byte (garbage)
241
                buffer = ByteHelper.extract(buffer, 0, buffer.length - 1);
242
            }
243
 
244
        }
245
 
246
        bits += nbBits;
247
        bits %= 8;
248
    }
249
 
250
    /**
251
     * Overwrite the content of CodecBuffer using the provided bytes
252
     * @param bytes     New content of the CodecBuffer
253
     * @see             setBits()
254
     */
255
    public void setBytes(byte[] bytes) {
256
        buffer = bytes.clone();
257
        bits = 0;
1192 berge 258
        markers.clear();
1133 berge 259
    }
260
 
261
    /**
262
     * Overwrite the content of CodecBuffer using the provided bits
263
     * @param bytes     Byte array used to store the bits to be used.  
264
     *                      It MUST be right-aligned. First byte (origBytes[0]) may be a
265
     *                      partial byte if 'nbBits' is not a multiple of 8.
266
     *                      In this case MSBs of this byte are ignored and not copied
267
     * @param nbBits        Number of significant bits in 'bytes'
268
     * @see             setBytes()
269
     */
270
    public void setBits(byte[] bytes, int nbBits) {
271
 
272
        if(nbBits == 0) {
273
            bits = 0;
274
            buffer = new byte[]{};
275
        }
276
        else {
277
            int i;
278
            int rbits = nbBits % 8;
279
            int nbBytes = nbBits / 8 + ((rbits > 0)?1:0);
280
            bits = rbits;
281
            buffer = bytes.clone();
282
 
283
            if(bits !=0) {
284
                for(i=(bytes.length - nbBytes); i < (nbBytes - 1); i++) {
285
                    buffer[i] = (byte)(buffer[i] << (8 - bits));
286
                    buffer[i] |= ((buffer[i+1] >>> bits) & nomasks[bits]);
287
                }
288
                buffer[i] = (byte)(buffer[i] << (8 - bits));
289
                buffer[i] &= masks[bits];
290
            }
291
        }
1192 berge 292
        markers.clear();
1133 berge 293
    }
294
 
295
    /**
296
     * Extracts some bytes at the beginning of the buffer. Read bytes are discarded from the buffer.
297
     * @param nbBytes   Number of bytes to be read
298
     * @return          Byte array containing the 'nbBytes' first bytes of the buffer.
299
     *                  Byte array's length may be shorter than requested if buffer does not
300
     *                  contain enough bytes
301
     * @see             getBytes()                
302
     */
303
    public byte[] readBytes(int nbBytes) {
304
        byte[] result = getBytes(0, nbBytes);
305
        int newLength = getNbBits() - (nbBytes * 8);
306
 
307
        if(result != null) {
308
            if(newLength > 0) {
309
                setBits(getBits(nbBytes * 8, newLength), newLength);
310
            }
311
            else {
312
                bits = 0;
313
                buffer = new byte[] {};
314
            }
2655 filatov 315
        } else {
316
            result = new byte[] {};
1133 berge 317
        }
318
        return result;
319
        // TODO: move markers
320
    }
321
 
322
    /**
323
     * Extracts some bits at the beginning of the buffer. Read bits are discarded from the buffer.
324
     * @param nbBytes   Number of bits to be read
325
     * @return          Byte array containing the 'nbBits' first bits of the buffer.
326
     *                  Number of returned bits may be smaller than requested if buffer does not
327
     *                  contain enough bits. Returned byte array is right-aligned.
328
     *                  First byte may be a partial byte if 'nbBits' is not a multiple of 8.
329
     *                  In this case MSBs of this byte are not significants and padded with '0's
330
     * @see             getBits()                
331
     */
332
    public byte[] readBits(int nbBits) {
333
        byte[] result = getBits(0, nbBits);
334
        int newLength = getNbBits() - nbBits;
335
        byte[] newBuffer = getBits(nbBits, newLength);
336
 
337
        if(result != null) {
338
            setBits(newBuffer, newLength);
339
        }
340
        return result;
341
        // TODO: move markers
342
    }
343
 
344
    /**
345
     * Retrieves the raw content of the CodecBuffer
346
     * @return  Raw byte array used to store CodecBuffer's content
347
     *          Returned byte array is left-aligned.
348
     *          Last byte may be a partial byte if 'bits' is not null.
349
     *          In this case LSBs of this byte are not significants and their value is undetermined
350
     */
351
    public byte[] getBytes() {
352
        return buffer;
353
    }
354
 
355
    /**
356
     * Retrieves some bytes from the CodecBuffer. Returned bytes are not removed from the buffer
357
     * @param start     Start point (octet index)
358
     * @param nbBytes   Number of bytes to be returned
359
     * @return          Extracted bytes.
360
     *                  Returned byte array is left-aligned.
361
     *                  Last byte may be a "partial byte" if it is the last byte of CodecBuffer and if 'bits' is not null.
362
     *                  In this case LSBs of this byte are not significants and their value is undetermined
363
     * @see             ReadBytes()
364
     */
365
    public byte[] getBytes(int start, int nbBytes) {
366
 
1247 berge 367
        if(start > buffer.length) {
2655 filatov 368
            TERFactory.getInstance().logDebug("bad start: " + start);
1133 berge 369
            return null;
370
        }
371
        if((start + nbBytes) > buffer.length) {
2655 filatov 372
            TERFactory.getInstance().logDebug("bad length: " + (start + nbBytes) + " (" + buffer.length + " bytes remaining)");
1133 berge 373
            return null;
374
        }
375
 
376
        if(nbBytes < 0) {
2655 filatov 377
            TERFactory.getInstance().logDebug("bad length: " + (nbBytes) + " (" + buffer.length + " bytes remaining)");
1133 berge 378
            return null;
379
        }
380
 
381
        return ByteHelper.extract(buffer, start, nbBytes);
382
    }
383
 
384
    /**
385
     * Retrieves all the bits from the CodecBuffer. Returned bits are not removed from the buffer
386
     * @return          Extracted bits stored in a byte array.
387
     *                  Returned byte array is right-aligned.
388
     *                  First byte may be a "partial byte" if 'nbBits' is not a multiple of 8.
389
     *                  In this case MSBs of this byte are not significants and their value is '0'
390
     * @see             ReadBits()
391
     */
392
    public byte[] getBits() {
393
        return getBits(0, getNbBits());
394
    }
395
 
396
    /**
397
     * Retrieves some bits from the CodecBuffer. Returned bits are not removed from the buffer
398
     * @param start     Start point (bit index)
399
     * @param nbBits    Number of bits to be returned
400
     * @return          Extracted bits stored in a byte array.
401
     *                  Returned byte array is right-aligned.
402
     *                  First byte may be a "partial byte" if 'nbBits' is not a multiple of 8.
403
     *                  In this case MSBs of this byte are not significants and their value is '0'
404
     * @see             ReadBits()
405
     */
406
    public byte[] getBits(int start, int nbBits) {
407
 
408
        int byteIndex = start / 8;
409
        int bitOffset = start % 8;
410
        int nbBytes = ((bitOffset > 0)?1:0) + (((nbBits - 8 + bitOffset) / 8) + ((bitOffset==0)?1:0)) + ((((nbBits - 8 + bitOffset) % 8) > 0)?1:0);
411
        //            leading partial byte    complete bytes                                            trailing partial byte  
412
 
1247 berge 413
        if(byteIndex > buffer.length) {
2655 filatov 414
            TERFactory.getInstance().logDebug("bad start: " + byteIndex + "(" + start + ")"  + " (" + buffer.length + " bytes remaining)");
1133 berge 415
            return null;
416
        }
417
        if((byteIndex + nbBytes) > buffer.length) {
2655 filatov 418
            TERFactory.getInstance().logDebug("bad length: " + (byteIndex + nbBytes) + "(" + nbBits + ")" + " (" + buffer.length + " bytes remaining)");
1133 berge 419
            return null;
420
        }
421
 
422
        byte[] tmp = ByteHelper.extract(buffer, byteIndex, nbBytes);
423
 
424
        if(bitOffset != 0) {
425
            tmp[0] = (byte)(tmp[0] << bitOffset);
426
            for(int i=1; i < tmp.length; i++) {
427
                tmp[i-1] &= masks[8 - bitOffset];
428
                tmp[i-1] |= ((tmp[i] >>> (8 - bitOffset)) & nomasks[8 - bitOffset]);
429
                tmp[i] = (byte)(tmp[i] << bitOffset);
430
            }
431
        }
432
 
433
        tmp = ByteHelper.extract(tmp, 0, nbBits / 8 + (((nbBits % 8) > 0)?1:0));
434
 
435
        if(nbBits % 8 > 0) {
436
            for(int i=tmp.length-1; i >= 0; i--) {
437
                tmp[i] = (byte)((tmp[i] >>> (8 - (nbBits % 8))) & nomasks[8 - (nbBits % 8)]);
438
                if(i > 0) {
439
                    tmp[i] |= (byte)(tmp[i-1] << (nbBits % 8));
440
                }
441
            }
442
        }
443
 
444
        return tmp;
445
    }
446
 
447
    /**
448
     * Retrieves some bits from the CodecBuffer. Returned bits are not removed from the buffer
1143 berge 449
     * @param markerKey Name of the marker serving as starting point for the extraction
1133 berge 450
     * @param nbBits    Number of bits to be returned
451
     * @return          Extracted bits stored in a byte array.
452
     *                  Returned byte array is right-aligned.
453
     *                  First byte may be a "partial byte" if 'nbBits' is not a multiple of 8.
454
     *                  In this case MSBs of this byte are not significants and their value is '0'
455
     * @see             ReadBits()
456
     */
457
    public byte[] getBits(String markerKey, int nbBits) {
458
        Marker marker = markers.get(markerKey);
459
 
460
        if(marker != null) {
461
            return getBits(marker.getPos(), nbBits);
462
        }
463
        return null;
464
    }
465
 
466
    /**
467
     * Private class used to represent markers that can be insterted in CodecBuffer
468
     */
469
    private class Marker {
470
 
471
        /**
472
         * Constructor.
473
         * @param pos       Position of the marker relative to the current position
474
         * @param callback  Optional callback object to be executed later, or null
475
         */
476
        public Marker(int pos, IMarkerCallback callback) {
477
            this.pos = pos;
478
            this.callback = callback;
479
        }
480
 
481
        /**
482
         * Retrieve the position of the marker (bit offset from buffer's start).
483
         * @return  The position of the marker
484
         */
485
        public int getPos() {
486
            return pos;
487
        }
488
 
489
        /**
490
         * Retrieve the callback object associated to the marker.
491
         * @return  The callback object associated to the marker, or null
492
         */
493
        public IMarkerCallback getCallback() {
494
            return callback;
495
        }
496
 
497
        /**
498
         * Changes the position of the marker in the buffer
499
         * @param nbBits    Position offset from marker's current position (can be negative)
500
         */
501
        public void move(int nbBits) {
502
            pos += nbBits;
503
        }
504
 
505
        /**
506
         * Current position of the marker
507
         */
508
        private int pos;
509
 
510
 
511
        /**
512
         * Callback object associated to the marker
513
         */
514
        private IMarkerCallback callback;
515
    }
516
}