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