    001    package com.skype.ipc;
    002
    003    import java.io.IOException;
    004
    005    public final class EventBuffer implements InputTransporting {
    006            private final int MIN_BUFFER_SIZE = 512;
    007
    008            public EventBuffer(int listMaxNested, int listMaxElem) {
    009                    mBuffer        = new byte[MIN_BUFFER_SIZE];
    010                    mBegin         = 0;
    011                    mEnd           = 0;
    012                    mSize          = 0;
    013                    mListMaxNested = listMaxNested;
    014                    mListMaxElem   = listMaxElem;
    015                    mListDepth     = 0;
    016            }
    017
    018            public synchronized boolean isEmpty() {
    019                    return mSize == 0;
    020            }
    021
    022            //
    023            // InputTransporting
    024            //
    025
    026            synchronized public void skipBytes(int numBytes) throws IOException {
    027                    int n        = numBytes;
    028                    int capacity = mBuffer.length;
    029                    assert(n <= mSize);
    030                    if ((mBegin+n) > capacity) {
    031                            int len = capacity - mBegin;
    032                            n      -= len;
    033                            mBegin  = 0;
    034                    }
    035                    mBegin = (mBegin+n)&(capacity-1);
    036                    mSize -= numBytes;
    037                    // shrink?
    038                    if (mSize < MIN_BUFFER_SIZE && capacity > MIN_BUFFER_SIZE)
    039                            resize(MIN_BUFFER_SIZE);
    040    //              return numBytes;
    041            }
    042
    043            synchronized public void readBytes(byte [] dest, int offset, int numBytes) throws IOException {
    044                    // one shall not read more than what is available as the payloads are fully buffered
    045                    // before getting accessible
    046                    int n = numBytes;
    047                    int capacity = mBuffer.length;
    048                    assert(n <= mSize);
    049                    if ((mBegin+n) > capacity) {
    050                            int len = capacity - mBegin;
    051                            System.arraycopy(mBuffer, mBegin, dest, offset, len);
    052                            mSize  -= len;
    053                            n      -= len;
    054                            offset += len;
    055                            mBegin  = 0;
    056                    }
    057                    System.arraycopy(mBuffer, mBegin, dest, offset, n);
    058                    mBegin = (mBegin+n)&(capacity-1);
    059                    mSize -= n;
    060                    // shrink?
    061                    if (mSize < MIN_BUFFER_SIZE && capacity > MIN_BUFFER_SIZE)
    062                            resize(MIN_BUFFER_SIZE);
    063            }
    064
    065            synchronized public int readByte() throws IOException {
    066                    assert(mSize > 0);
    067                    int capacity = mBuffer.length;
    068                    int b = mBuffer[mBegin];
    069                    mBegin = (mBegin+1)&(capacity-1);
    070                    mSize--;
    071                    if (mSize < MIN_BUFFER_SIZE && capacity > MIN_BUFFER_SIZE)
    072                            resize(MIN_BUFFER_SIZE);
    073                    return b;
    074            }
    075
    076            public void readBytes(byte[] dest) throws IOException {
    077                    readBytes(dest, 0, dest.length);
    078            }
    079
    080            private void grow(int minSize) {
    081                    int capacity = mBuffer.length+mBuffer.length;
    082                    while (capacity < minSize) {
    083                            capacity = capacity + capacity;
    084                    }
    085                    resize(capacity);
    086            }
    087
    088            private void resize(int capacity) {
    089                    byte[] newBuffer = new byte[capacity];
    090                    if (mSize > 0) {
    091                            if (mBegin < mEnd) {
    092                                    System.arraycopy(mBuffer, mBegin, newBuffer, 0, mSize);
    093                            } else {
    094                                    System.arraycopy(mBuffer, mBegin, newBuffer, 0, mBuffer.length-mBegin);
    095                                    System.arraycopy(mBuffer, 0, newBuffer, mBuffer.length-mBegin, mEnd);
    096                            }
    097                    }
    098                    mBegin  = 0;
    099                    mEnd    = mSize;
    100                    mBuffer = newBuffer;
    101            }
    102
    103            private int putByte(int b) {
    104                    if (mSize == mBuffer.length)
    105                            grow(mBuffer.length+1);
    106                    mBuffer[mEnd++] = (byte) b;
    107                    mEnd &= mBuffer.length-1;
    108                    mSize++;
    109                    return b;
    110            }
    111
    112            private void putBytes(int n, InputTransporting reader) throws IOException {
    113                    if ((mSize+n) > mBuffer.length)
    114                            grow(mSize+n);
    115                    if ((mEnd+n) > mBuffer.length) {
    116                            int m = mBuffer.length - mEnd;
    117                            reader.readBytes(mBuffer, mEnd, m);
    118                            mEnd   = 0;
    119                            mSize += m;
    120                            n     -= m;
    121                    }
    122                    reader.readBytes(mBuffer, mEnd, n);
    123                    mEnd  += n;
    124                    if (mEnd == mBuffer.length) mEnd = 0;
    125                    mSize += n;
    126            }
    127
    128            private int bufferUint(InputTransporting transport) throws IOException {
    129                    int shift = 0;
    130                    int result = 0;
    131                    while (true) {
    132                            int value = putByte(transport.readByte()) & 0xFF;
    133                            result = result | ((value & 0x7f) << shift);
    134                            shift = shift + 7;
    135                            if ((value & 0x80) == 0)
    136                                    break;
    137                    }
    138                    return result;
    139            }
    140
    141            private long bufferUint64(InputTransporting transport) throws IOException {
    142                    int shift = 0;
    143                    long result = 0;
    144                    while (true) {
    145                            int value = putByte(transport.readByte()) & 0xFF;
    146                            result = result | ((value & 0x7f) << shift);
    147                            shift = shift + 7;
    148                            if ((value & 0x80) == 0)
    149                                    break;
    150                    }
    151                    return result;
    152            }
    153
    154            private void bufferValue(int kind, InputTransporting transport) throws IOException {
    155                    switch (kind) {
    156                    case 'i': case 'O': case 'u': case 'e': case 'b':
    157                            bufferUint(transport);
    158                            return;
    159                    case 'T': case 'F': case 'N':
    160                            return;
    161                    case 'U':
    162                            bufferUint64(transport);
    163                            return;
    164                    case 'S': case 'X': case 'f': case 'B':
    165                            putBytes(bufferUint(transport), transport);
    166                            return;
    167                    case '[': {
    168                            if (mListDepth++ > mListMaxNested) throw new ProtocolException("listDepth");
    169                            int elemKind = putByte(transport.readByte());
    170                            int numElem  = 0;
    171                            while (elemKind != ']') {
    172                                    bufferValue(elemKind, transport);
    173                                    if (numElem++ > mListMaxElem) throw new ProtocolException("list too large");
    174                            }
    175                            mListDepth--;
    176                            return;
    177                    }
    178                    default:
    179                            throw new ProtocolException("unknown kind");
    180                    }
    181            }
    182
    183            public synchronized void bufferEvent(InputTransporting transport) throws IOException {
    184                    putByte('Z');
    185                    putByte('E');
    186                    bufferUint(transport); // moduleId
    187                    bufferUint(transport); // eventId
    188                    do {
    189                            int kind = putByte(transport.readByte());
    190                            if (kind == 'z') return;
    191                            bufferUint(transport);
    192                            bufferValue(kind, transport);
    193                    } while (true);
    194            }
    195
    196            public synchronized void bufferChange(InputTransporting transport) throws IOException {
    197                    putByte('Z');
    198                    putByte('C');
    199                    bufferUint(transport); // moduleId
    200                    bufferUint(transport); // oid
    201                    boolean valueExpected = true;
    202                    do {
    203                            int sign = putByte(transport.readByte()); // next property?
    204                            if (sign == ']') { // end of property
    205                                    if (valueExpected) {
    206                                             throw new ProtocolException("expecting a value");
    207                                    }
    208                                    sign = putByte(transport.readByte());
    209                                    if (sign == ']') { // end objects
    210                                            sign = putByte(transport.readByte());
    211                                            if (sign == ']') {
    212                                                    sign = putByte(transport.readByte());
    213                                                    if (sign != 'z') throw new ProtocolException("change shall terminate");
    214                                                    return;
    215                                            } else if (sign == ',') { // next module
    216                                                    bufferUint(transport); // moduleId
    217                                                    bufferUint(transport); // oid
    218                                                    valueExpected = true;
    219                                            } else {
    220                                                    throw new ProtocolException("");
    221                                            }
    222                                    } else if (sign == ',') { // next object
    223                                            bufferUint(transport); // oid
    224                                            valueExpected = true;
    225                                    } else {
    226                                            throw new ProtocolException("");
    227                                    }
    228                            } else { // next property
    229                                    bufferUint(transport);
    230                                    bufferValue(sign, transport);
    231                                    valueExpected = false;
    232                            }
    233                    } while (true);
    234            }
    235
    236            public void close() throws IOException {
    237                    mSize = 0;
    238            }
    239
    240            byte[] mBuffer;
    241            int    mBegin;
    242            int    mEnd;
    243            int    mSize;
    244            int    mListDepth;
    245            int    mListMaxNested;
    246            int    mListMaxElem;
    247    }
    248