PageRenderTime 38ms CodeModel.GetById 14ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/protocols/smpp/src/main/java/org/mobicents/protocols/smpp/gsm/ems/CompressedData.java

http://mobicents.googlecode.com/
Java | 180 lines | 135 code | 13 blank | 32 comment | 30 complexity | 7862aa4744c7d1ca3ef9e87f849defed MD5 | raw file
  1/*
  2 * JBoss, Home of Professional Open Source
  3 * Copyright 2011, Red Hat, Inc. and individual contributors
  4 * by the @authors tag. See the copyright.txt in the distribution for a
  5 * full listing of individual contributors.
  6 *
  7 * This is free software; you can redistribute it and/or modify it
  8 * under the terms of the GNU Lesser General Public License as
  9 * published by the Free Software Foundation; either version 2.1 of
 10 * the License, or (at your option) any later version.
 11 *
 12 * This software is distributed in the hope that it will be useful,
 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15 * Lesser General Public License for more details.
 16 *
 17 * You should have received a copy of the GNU Lesser General Public
 18 * License along with this software; if not, write to the Free
 19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 21 */
 22
 23package org.mobicents.protocols.smpp.gsm.ems;
 24
 25import java.nio.ByteBuffer;
 26import java.util.ArrayList;
 27import java.util.List;
 28
 29import org.mobicents.protocols.smpp.gsm.AbstractHeaderElement;
 30
 31/**
 32 * Header element that represents one or more compressed extended
 33 * objects.
 34 * @version $Id: CompressedData.java 484 2010-02-08 16:08:50Z orank $
 35 */
 36public class CompressedData extends AbstractHeaderElement {
 37
 38    private List<ExtendedObject> objects = new ArrayList<ExtendedObject>();
 39    private byte[] compressedData;
 40    private int ptr;
 41
 42    public void add(ExtendedObject object) {
 43        objects.add(object);
 44    }
 45
 46    public byte[] compress() {
 47        int length = 0;
 48        for (ExtendedObject obj : objects) {
 49            length += obj.getLength();
 50        }
 51        length += objects.size();
 52        // Create the uncompressed buffer.
 53        ByteBuffer buffer = ByteBuffer.allocate(length);
 54        for (ExtendedObject obj : objects) {
 55            buffer.put((byte) 0x14);
 56            buffer.put(obj.getData());
 57        }
 58        byte[] uncompressed = buffer.array();
 59        ByteBuffer compressed = ByteBuffer.allocate(length);
 60        ByteBuffer literal = ByteBuffer.allocate(length);
 61        literal.put(uncompressed, 0, 2);
 62        int readPos = 2;
 63        while (readPos < length) {
 64            boolean sliceFound = false;
 65            // Can't find a slice size larger than the number of bytes we
 66            // have available behind or in front of the current read position.
 67            int sliceSize =
 68                Math.min(Math.min(63, readPos), uncompressed.length - readPos);
 69            int slicePos = -1;
 70            for (; sliceSize > 2; sliceSize--) {
 71                slicePos = findSlice(uncompressed, readPos, sliceSize);
 72                if (slicePos >= 0) {
 73                    sliceFound = true;
 74                    break;
 75                }
 76            }
 77            if (!sliceFound) {
 78                // Add the current byte to the literal buffer
 79                literal.put((byte) uncompressed[readPos]);
 80                readPos++;
 81            } else {
 82                // Output the current literal buffer.
 83                outputLiterals(compressed, literal);
 84                // Output a slice descriptor
 85                slicePos = readPos - slicePos;
 86                int descriptor = ((sliceSize & 0x2f) << 9) | (slicePos & 0x1ff);
 87                compressed.putShort((short) descriptor);
 88                readPos += sliceSize;
 89            }
 90        }
 91        compressed.flip();
 92        compressedData = new byte[compressed.remaining()];
 93        compressed.get(compressedData, 0, compressed.remaining());
 94        return compressedData;
 95    }
 96    
 97    @Override
 98    public void reset() {
 99        super.reset();
100        objects.clear();
101        compressedData = null;
102        ptr = 0;
103    }
104    
105    public int getLength() {
106        if (compressedData == null) {
107            throw new IllegalStateException("Must compress the data first.");
108        }
109        return compressedData.length + 3;
110    }
111
112    @Override
113    public boolean isComplete() {
114        if (compressedData == null) {
115            throw new IllegalStateException("Must compress the data first.");
116        }
117        return ptr == compressedData.length;
118    }
119    
120    @Override
121    public boolean write(int segmentNum, ByteBuffer buffer) {
122        if (compressedData == null) {
123            throw new IllegalStateException("Must compress the data first.");
124        }
125        boolean writeHeader = ptr == 0;
126        if (writeHeader && buffer.remaining() < 5) {
127            return false;
128        } else if (!writeHeader && buffer.remaining() < 3) {
129            return false;
130        }
131        int headerSize;
132        int dataSize;
133        if (writeHeader) {
134            headerSize = 3;
135            dataSize = Math.min(buffer.remaining() - 5, compressedData.length - ptr);
136        } else {
137            headerSize = 0;
138            dataSize = Math.min(buffer.remaining() - 2, compressedData.length - ptr);
139        }
140        buffer.put((byte) 0x16);
141        buffer.put((byte) (headerSize + dataSize));
142        if (writeHeader) {
143            buffer.put((byte) 0);
144            buffer.putShort((short) compressedData.length);
145        }
146        buffer.put(compressedData, ptr, dataSize);
147        ptr += dataSize;
148        return true;
149    }
150    
151    private int findSlice(byte[] data, int readPos, int sliceSize) {
152        int pos = readPos - sliceSize;
153        int endPos = Math.max(readPos - 511, 0);
154        for (; pos >= endPos; pos--) {
155            boolean matched = true;
156            for (int i = 0; i < sliceSize; i++) {
157                if (data[readPos + i] != data[pos + i]) {
158                    matched = false;
159                    break;
160                }
161            }
162            if (matched) {
163                return pos;
164            }
165        }
166        return -1;
167    }
168    
169    private void outputLiterals(ByteBuffer compressed, ByteBuffer literal) {
170        literal.flip();
171        while (literal.remaining() > 0) {
172            int size = Math.min(127, literal.remaining());
173            compressed.put((byte) (0x80 | size));
174            for (int i = 0; i < size; i++) {
175                compressed.put(literal.get());
176            }
177        }
178        literal.clear();
179    }
180}