mirror of
https://github.com/ethauvin/JSON-java.git
synced 2025-06-17 16:00:51 -07:00
Java 1.8.
This commit is contained in:
parent
48d31b7f5c
commit
a9a0762383
26 changed files with 747 additions and 819 deletions
|
@ -30,15 +30,10 @@ import java.io.InputStream;
|
|||
/**
|
||||
* This is a big endian bit reader. It reads its bits from an InputStream.
|
||||
*
|
||||
* @version 2013-04-18
|
||||
* @version 2013-05-03
|
||||
*
|
||||
*/
|
||||
public class BitInputStream implements BitReader {
|
||||
/**
|
||||
* 2^n - 1
|
||||
*/
|
||||
static final int[] mask = { 0, 1, 3, 7, 15, 31, 63, 127, 255 };
|
||||
|
||||
/**
|
||||
* The number of bits remaining in the current byte.
|
||||
*/
|
||||
|
@ -70,23 +65,6 @@ public class BitInputStream implements BitReader {
|
|||
this.in = in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a BitReader. The first byte is passed in explicitly, the remaining
|
||||
* bytes are obtained from the InputStream. This makes it possible to look
|
||||
* at the first byte of a stream before deciding that it should be read as
|
||||
* bits.
|
||||
*
|
||||
* @param in
|
||||
* An InputStream
|
||||
* @param firstByte
|
||||
* The first byte, which was probably read from in.
|
||||
*/
|
||||
public BitInputStream(InputStream in, int firstByte) {
|
||||
this.in = in;
|
||||
this.unread = firstByte;
|
||||
this.available = 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one bit.
|
||||
*
|
||||
|
@ -111,20 +89,26 @@ public class BitInputStream implements BitReader {
|
|||
/**
|
||||
* Check that the rest of the block has been padded with zeroes.
|
||||
*
|
||||
* @param factor
|
||||
* The size of the block to pad. This will typically be 8, 16,
|
||||
* 32, 64, 128, 256, etc.
|
||||
* @param width
|
||||
* The size of the block to pad in bits.
|
||||
* This will typically be 8, 16, 32, 64, 128, 256, etc.
|
||||
* @return true if the block was zero padded, or false if the the padding
|
||||
* contains any one bits.
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean pad(int factor) throws IOException {
|
||||
int padding = factor - (int) (this.nrBits % factor);
|
||||
public boolean pad(int width) throws IOException {
|
||||
boolean result = true;
|
||||
|
||||
for (int i = 0; i < padding; i += 1) {
|
||||
if (bit()) {
|
||||
result = false;
|
||||
int gap = (int)this.nrBits % width;
|
||||
if (gap < 0) {
|
||||
gap += width;
|
||||
}
|
||||
if (gap != 0) {
|
||||
int padding = width - gap;
|
||||
while (padding > 0) {
|
||||
if (bit()) {
|
||||
result = false;
|
||||
}
|
||||
padding -= 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -158,8 +142,8 @@ public class BitInputStream implements BitReader {
|
|||
if (take > this.available) {
|
||||
take = this.available;
|
||||
}
|
||||
result |= ((this.unread >>> (this.available - take)) & mask[take])
|
||||
<< (width - take);
|
||||
result |= ((this.unread >>> (this.available - take)) &
|
||||
((1 << take) - 1)) << (width - take);
|
||||
this.nrBits += take;
|
||||
this.available -= take;
|
||||
width -= take;
|
||||
|
|
|
@ -30,7 +30,7 @@ import java.io.OutputStream;
|
|||
/**
|
||||
* This is a big endian bit writer. It writes its bits to an OutputStream.
|
||||
*
|
||||
* @version 2013-04-18
|
||||
* @version 2013-05-03
|
||||
*
|
||||
*/
|
||||
public class BitOutputStream implements BitWriter {
|
||||
|
@ -85,25 +85,25 @@ public class BitOutputStream implements BitWriter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Pad the rest of the block with zeroes and flush. pad(8) flushes the last
|
||||
* Pad the rest of the block with zeros and flush. pad(8) flushes the last
|
||||
* unfinished byte. The underlying OutputStream will be flushed.
|
||||
*
|
||||
* @param factor
|
||||
* The size of the block to pad. This will typically be 8, 16,
|
||||
* 32, 64, 128, 256, etc.
|
||||
* @return this
|
||||
* @param width
|
||||
* The size of the block to pad in bits.
|
||||
* This will typically be 8, 16, 32, 64, 128, 256, etc.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void pad(int factor) throws IOException {
|
||||
int padding = factor - (int) (nrBits % factor);
|
||||
int excess = padding & 7;
|
||||
if (excess > 0) {
|
||||
this.write(0, excess);
|
||||
padding -= excess;
|
||||
public void pad(int width) throws IOException {
|
||||
int gap = (int)this.nrBits % width;
|
||||
if (gap < 0) {
|
||||
gap += width;
|
||||
}
|
||||
while (padding > 0) {
|
||||
this.write(0, 8);
|
||||
padding -= 8;
|
||||
if (gap != 0) {
|
||||
int padding = width - gap;
|
||||
while (padding > 0) {
|
||||
this.zero();
|
||||
padding -= 1;
|
||||
}
|
||||
}
|
||||
this.out.flush();
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ public class BitOutputStream implements BitWriter {
|
|||
actual = this.vacant;
|
||||
}
|
||||
this.unwritten |= ((bits >>> (width - actual)) &
|
||||
BitInputStream.mask[actual]) << (this.vacant - actual);
|
||||
((1 << actual) - 1)) << (this.vacant - actual);
|
||||
width -= actual;
|
||||
nrBits += actual;
|
||||
this.vacant -= actual;
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.json.zip;
|
|||
import java.io.IOException;
|
||||
|
||||
public interface BitReader {
|
||||
|
||||
/**
|
||||
* Read one bit.
|
||||
*
|
||||
|
@ -18,16 +19,16 @@ public interface BitReader {
|
|||
public long nrBits();
|
||||
|
||||
/**
|
||||
* Check that the rest of the block has been padded with zeroes.
|
||||
* Check that the rest of the block has been padded with zeros.
|
||||
*
|
||||
* @param factor
|
||||
* @param width
|
||||
* The size in bits of the block to pad. This will typically be
|
||||
* 8, 16, 32, 64, 128, 256, etc.
|
||||
* @return true if the block was zero padded, or false if the the padding
|
||||
* contained any one bits.
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean pad(int factor) throws IOException;
|
||||
public boolean pad(int width) throws IOException;
|
||||
|
||||
/**
|
||||
* Read some bits.
|
||||
|
|
|
@ -7,10 +7,6 @@ import java.io.IOException;
|
|||
* Most IO interfaces only allow for writing at the byte level or higher.
|
||||
*/
|
||||
public interface BitWriter {
|
||||
/**
|
||||
* Returns the number of bits that have been written to this bitwriter.
|
||||
*/
|
||||
public long nrBits();
|
||||
|
||||
/**
|
||||
* Write a 1 bit.
|
||||
|
@ -22,14 +18,12 @@ public interface BitWriter {
|
|||
/**
|
||||
* Pad the rest of the block with zeros and flush.
|
||||
*
|
||||
* @param factor
|
||||
* @param width
|
||||
* The size in bits of the block to pad. This will typically be
|
||||
* 8, 16, 32, 64, 128, 256, etc.
|
||||
* @return true if the block was zero padded, or false if the the padding
|
||||
* contains any one bits.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void pad(int factor) throws IOException;
|
||||
public void pad(int width) throws IOException;
|
||||
|
||||
/**
|
||||
* Write some bits. Up to 32 bits can be written at a time.
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.json.JSONException;
|
|||
/**
|
||||
* JSONzip is a compression scheme for JSON text.
|
||||
* @author JSON.org
|
||||
* @version 2013-04-18
|
||||
* @version 2014-05-03
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -42,6 +42,9 @@ import org.json.JSONException;
|
|||
* symbol is incremented by the tick method. The generate method is used to
|
||||
* generate the encoding table. The table must be generated before encoding or
|
||||
* decoding. You may regenerate the table with the latest weights at any time.
|
||||
*
|
||||
* After a million ticks, it is assumed that the distribution is well
|
||||
* understood and that no more regeneration will be required.
|
||||
*/
|
||||
public class Huff implements None, PostMortem {
|
||||
|
||||
|
@ -60,6 +63,11 @@ public class Huff implements None, PostMortem {
|
|||
*/
|
||||
private Symbol table;
|
||||
|
||||
/**
|
||||
* The number of characters left to learn to adapt the coding table.
|
||||
*/
|
||||
private int toLearn;
|
||||
|
||||
/**
|
||||
* Have any weights changed since the table was last generated?
|
||||
*/
|
||||
|
@ -100,7 +108,7 @@ public class Huff implements None, PostMortem {
|
|||
if (this.integer != that.integer || this.weight != that.weight) {
|
||||
return false;
|
||||
}
|
||||
if ((this.back != null) != (that.back != null)) {
|
||||
if ((this.back == null) != (that.back == null)) {
|
||||
return false;
|
||||
}
|
||||
Symbol zero = this.zero;
|
||||
|
@ -132,6 +140,7 @@ public class Huff implements None, PostMortem {
|
|||
*/
|
||||
public Huff(int domain) {
|
||||
this.domain = domain;
|
||||
this.toLearn = 1000000;
|
||||
int length = domain * 2 - 1;
|
||||
this.symbols = new Symbol[length];
|
||||
|
||||
|
@ -141,7 +150,7 @@ public class Huff implements None, PostMortem {
|
|||
symbols[i] = new Symbol(i);
|
||||
}
|
||||
|
||||
// SMake the links.
|
||||
// Make the links.
|
||||
|
||||
for (int i = domain; i < length; i += 1) {
|
||||
symbols[i] = new Symbol(none);
|
||||
|
@ -151,8 +160,6 @@ public class Huff implements None, PostMortem {
|
|||
/**
|
||||
* Generate the encoding/decoding table. The table determines the bit
|
||||
* sequences used by the read and write methods.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public void generate() {
|
||||
if (!this.upToDate) {
|
||||
|
@ -176,8 +183,8 @@ public class Huff implements None, PostMortem {
|
|||
head = symbol;
|
||||
} else {
|
||||
|
||||
// To save time, we will start the search from the previous symbol instead
|
||||
// of the head unless the current symbol weights less than the previous symbol.
|
||||
// We will start the search from the previous symbol instead of the head unless
|
||||
// the current symbol weights less than the previous symbol.
|
||||
|
||||
if (symbol.weight < previous.weight) {
|
||||
previous = head;
|
||||
|
@ -290,7 +297,7 @@ public class Huff implements None, PostMortem {
|
|||
public boolean postMortem(PostMortem pm) {
|
||||
|
||||
// Go through every integer in the domain, generating its bit sequence, and
|
||||
// then proving that that bit sequence produces the same integer.
|
||||
// then prove that that bit sequence produces the same integer.
|
||||
|
||||
for (int integer = 0; integer < this.domain; integer += 1) {
|
||||
if (!postMortem(integer)) {
|
||||
|
@ -330,29 +337,16 @@ public class Huff implements None, PostMortem {
|
|||
}
|
||||
|
||||
/**
|
||||
* Increase by 1 the weight associated with a value.
|
||||
* Increase the weight associated with a value by 1.
|
||||
*
|
||||
* @param value
|
||||
* The number of the symbol to tick
|
||||
* @return this
|
||||
*/
|
||||
public void tick(int value) {
|
||||
this.symbols[value].weight += 1;
|
||||
this.upToDate = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase by 1 the weight associated with a range of values.
|
||||
*
|
||||
* @param from
|
||||
* The first symbol to tick
|
||||
* @param to
|
||||
* The last symbol to tick
|
||||
* @return this
|
||||
*/
|
||||
public void tick(int from, int to) {
|
||||
for (int value = from; value <= to; value += 1) {
|
||||
tick(value);
|
||||
if (this.toLearn > 0) {
|
||||
this.toLearn -= 1;
|
||||
this.symbols[value].weight += 1;
|
||||
this.upToDate = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,7 +386,6 @@ public class Huff implements None, PostMortem {
|
|||
* The number of the symbol to write
|
||||
* @param bitwriter
|
||||
* The destination of the bits.
|
||||
* @return this
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void write(int value, BitWriter bitwriter) throws JSONException {
|
||||
|
|
|
@ -28,8 +28,8 @@ package org.json.zip;
|
|||
* JSONzip is a binary-encoded JSON dialect. It is designed to compress the
|
||||
* messages in a session in bandwidth constrained applications, such as mobile.
|
||||
*
|
||||
* JSONzip is adaptive, so with each message seen, it should
|
||||
* improve its compression. It minimizes JSON's overhead, reducing punctuation
|
||||
* JSONzip is adaptive, so with each message seen, it should improve its
|
||||
* compression. It minimizes JSON's overhead, reducing punctuation
|
||||
* to a small number of bits. It uses Huffman encoding to reduce the average
|
||||
* size of characters. It uses caches (or Keeps) to keep recently seen strings
|
||||
* and values, so repetitive content (such as object keys) can be
|
||||
|
@ -44,17 +44,9 @@ package org.json.zip;
|
|||
* ADEQUATELY FOR PRODUCTION USE.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2014-04-28
|
||||
* @version 2014-05-03
|
||||
*/
|
||||
public abstract class JSONzip implements None, PostMortem {
|
||||
/**
|
||||
* Powers of 2.
|
||||
*/
|
||||
public static final int[] twos = {
|
||||
1, 2, 4, 8, 16, 32, 64, 128, 256, 512,
|
||||
1024, 2048, 4096, 8192, 16384, 32768, 65536
|
||||
};
|
||||
|
||||
/**
|
||||
* The characters in JSON numbers can be reduced to 4 bits each.
|
||||
*/
|
||||
|
@ -87,28 +79,11 @@ public abstract class JSONzip implements None, PostMortem {
|
|||
*/
|
||||
public static final int endOfNumber = bcd.length;
|
||||
|
||||
/**
|
||||
* The maximum substring length when registering many. The registration of
|
||||
* one substring may be longer.
|
||||
*/
|
||||
public static final int maxSubstringLength = 10;
|
||||
|
||||
/**
|
||||
* The minimum substring length.
|
||||
*/
|
||||
public static final int minSubstringLength = 3;
|
||||
|
||||
/**
|
||||
* The package supports tracing for debugging.
|
||||
*/
|
||||
public static final boolean probe = false;
|
||||
|
||||
/**
|
||||
* The maximum number of substrings added to the substrings keep per
|
||||
* string.
|
||||
*/
|
||||
public static final int substringLimit = 40;
|
||||
|
||||
/**
|
||||
* The value code for an empty object.
|
||||
*/
|
||||
|
@ -155,62 +130,55 @@ public abstract class JSONzip implements None, PostMortem {
|
|||
protected final Huff namehuff;
|
||||
|
||||
/**
|
||||
* A place to keep the names (keys).
|
||||
* A Huffman encoder for names extended bytes.
|
||||
*/
|
||||
protected final MapKeep namekeep;
|
||||
protected final Huff namehuffext;
|
||||
|
||||
/**
|
||||
* A place to keep the strings.
|
||||
* A place to keep the names (keys).
|
||||
*/
|
||||
protected final MapKeep stringkeep;
|
||||
protected final Keep namekeep;
|
||||
|
||||
/**
|
||||
* A Huffman encoder for string values.
|
||||
*/
|
||||
protected final Huff substringhuff;
|
||||
protected final Huff stringhuff;
|
||||
|
||||
/**
|
||||
* A Huffman encoder for string values extended bytes.
|
||||
*/
|
||||
protected final Huff stringhuffext;
|
||||
|
||||
/**
|
||||
* A place to keep the strings.
|
||||
*/
|
||||
protected final TrieKeep substringkeep;
|
||||
protected final Keep stringkeep;
|
||||
|
||||
/**
|
||||
* A place to keep the values.
|
||||
*/
|
||||
protected final MapKeep values;
|
||||
protected final Keep valuekeep;
|
||||
|
||||
/**
|
||||
* Initialize the data structures.
|
||||
*/
|
||||
protected JSONzip() {
|
||||
this.namehuff = new Huff(end + 1);
|
||||
this.namekeep = new MapKeep(9);
|
||||
this.stringkeep = new MapKeep(11);
|
||||
this.substringhuff = new Huff(end + 1);
|
||||
this.substringkeep = new TrieKeep(12);
|
||||
this.values = new MapKeep(10);
|
||||
|
||||
// Increase the weights of the ASCII letters, digits, and special characters
|
||||
// because they are highly likely to occur more frequently. The weight of each
|
||||
// character will increase as it is used. The Huffman encoder will tend to
|
||||
// use fewer bits to encode heavier characters.
|
||||
|
||||
this.namehuff.tick(' ', '}');
|
||||
this.namehuff.tick('a', 'z');
|
||||
this.namehuff.tick(end);
|
||||
this.namehuff.tick(end);
|
||||
this.substringhuff.tick(' ', '}');
|
||||
this.substringhuff.tick('a', 'z');
|
||||
this.substringhuff.tick(end);
|
||||
this.substringhuff.tick(end);
|
||||
this.namehuffext = new Huff(end + 1);
|
||||
this.namekeep = new Keep(9);
|
||||
this.stringhuff = new Huff(end + 1);
|
||||
this.stringhuffext = new Huff(end + 1);
|
||||
this.stringkeep = new Keep(11);
|
||||
this.valuekeep = new Keep(10);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Generate the Huffman tables.
|
||||
*/
|
||||
protected void begin() {
|
||||
protected void generate() {
|
||||
this.namehuff.generate();
|
||||
this.substringhuff.generate();
|
||||
this.stringhuff.generate();
|
||||
this.stringhuffext.generate();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,7 +191,7 @@ public abstract class JSONzip implements None, PostMortem {
|
|||
/**
|
||||
* Write an integer to the console.
|
||||
*
|
||||
* @param integer
|
||||
* @param integer The integer to write to the log.
|
||||
*/
|
||||
static void log(int integer) {
|
||||
log(integer + " ");
|
||||
|
@ -233,8 +201,8 @@ public abstract class JSONzip implements None, PostMortem {
|
|||
* Write two integers, separated by ':' to the console.
|
||||
* The second integer is suppressed if it is 1.
|
||||
*
|
||||
* @param integer
|
||||
* @param width
|
||||
* @param integer The integer to write to the log.
|
||||
* @param width The width of the integer in bits.
|
||||
*/
|
||||
static void log(int integer, int width) {
|
||||
if (width == 1) {
|
||||
|
@ -247,7 +215,7 @@ public abstract class JSONzip implements None, PostMortem {
|
|||
/**
|
||||
* Write a string to the console.
|
||||
*
|
||||
* @param string
|
||||
* @param string The string to be written to the log.
|
||||
*/
|
||||
static void log(String string) {
|
||||
System.out.print(string);
|
||||
|
@ -256,8 +224,8 @@ public abstract class JSONzip implements None, PostMortem {
|
|||
/**
|
||||
* Write a character or its code to the console.
|
||||
*
|
||||
* @param integer
|
||||
* @param width
|
||||
* @param integer The charcode to be written to the log.
|
||||
* @param width The width of the charcode in bits.
|
||||
*/
|
||||
static void logchar(int integer, int width) {
|
||||
if (integer > ' ' && integer <= '}') {
|
||||
|
@ -280,8 +248,7 @@ public abstract class JSONzip implements None, PostMortem {
|
|||
return this.namehuff.postMortem(that.namehuff)
|
||||
&& this.namekeep.postMortem(that.namekeep)
|
||||
&& this.stringkeep.postMortem(that.stringkeep)
|
||||
&& this.substringhuff.postMortem(that.substringhuff)
|
||||
&& this.substringkeep.postMortem(that.substringkeep)
|
||||
&& this.values.postMortem(that.values);
|
||||
&& this.stringhuff.postMortem(that.stringhuff)
|
||||
&& this.valuekeep.postMortem(that.valuekeep);
|
||||
}
|
||||
}
|
||||
|
|
137
zip/Keep.java
137
zip/Keep.java
|
@ -1,5 +1,8 @@
|
|||
package org.json.zip;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.json.Kim;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013 JSON.org
|
||||
|
@ -30,30 +33,34 @@ package org.json.zip;
|
|||
* numbers. This allows the sending of small integers instead of strings.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2013-04-18
|
||||
* @version 2013-05-03
|
||||
*/
|
||||
abstract class Keep implements None, PostMortem {
|
||||
protected int capacity;
|
||||
class Keep implements None, PostMortem {
|
||||
private int capacity;
|
||||
protected int length;
|
||||
protected int power;
|
||||
protected long[] uses;
|
||||
private Object[] list;
|
||||
private HashMap<Object, Integer> map;
|
||||
private int power;
|
||||
private long[] ticks;
|
||||
|
||||
public Keep(int bits) {
|
||||
this.capacity = JSONzip.twos[bits];
|
||||
this.capacity = 1 << bits;
|
||||
this.length = 0;
|
||||
this.power = 0;
|
||||
this.uses = new long[this.capacity];
|
||||
}
|
||||
this.ticks = new long[this.capacity];
|
||||
this.list = new Object[this.capacity];
|
||||
this.map = new HashMap<Object, Integer>(this.capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* When an item ages, its use count is reduced by at least half.
|
||||
*
|
||||
* @param use
|
||||
* @param ticks
|
||||
* The current use count of an item.
|
||||
* @return The new use count for that item.
|
||||
*/
|
||||
public static long age(long use) {
|
||||
return use >= 32 ? 16 : use / 2;
|
||||
public static long age(long ticks) {
|
||||
return ticks >= 32 ? 16 : ticks / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,7 +69,7 @@ abstract class Keep implements None, PostMortem {
|
|||
* required to identify one of its items goes up.
|
||||
*/
|
||||
public int bitsize() {
|
||||
while (JSONzip.twos[this.power] < this.length) {
|
||||
while (1 << this.power < this.length) {
|
||||
this.power += 1;
|
||||
}
|
||||
return this.power;
|
||||
|
@ -72,13 +79,113 @@ abstract class Keep implements None, PostMortem {
|
|||
* Increase the usage count on an integer value.
|
||||
*/
|
||||
public void tick(int integer) {
|
||||
this.uses[integer] += 1;
|
||||
this.ticks[integer] += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value associated with an integer.
|
||||
* Compact the keep. A keep may contain at most this.capacity elements.
|
||||
* The keep contents can be reduced by deleting all elements with low use
|
||||
* counts, and by reducing the use counts of the survivors.
|
||||
*/
|
||||
private void compact() {
|
||||
int from = 0;
|
||||
int to = 0;
|
||||
while (from < this.capacity) {
|
||||
Object key = this.list[from];
|
||||
long usage = age(this.ticks[from]);
|
||||
if (usage > 0) {
|
||||
this.ticks[to] = usage;
|
||||
this.list[to] = key;
|
||||
this.map.put(key, to);
|
||||
to += 1;
|
||||
} else {
|
||||
this.map.remove(key);
|
||||
}
|
||||
from += 1;
|
||||
}
|
||||
if (to < this.capacity) {
|
||||
this.length = to;
|
||||
} else {
|
||||
this.map.clear();
|
||||
this.length = 0;
|
||||
}
|
||||
this.power = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the integer value associated with this key, or nothing if this key
|
||||
* is not in the keep.
|
||||
*
|
||||
* @param key
|
||||
* An object.
|
||||
* @return An integer
|
||||
*/
|
||||
public int find(Object key) {
|
||||
Object o = this.map.get(key);
|
||||
return o instanceof Integer ? ((Integer) o).intValue() : none;
|
||||
}
|
||||
|
||||
public boolean postMortem(PostMortem pm) {
|
||||
Keep that = (Keep) pm;
|
||||
if (this.length != that.length) {
|
||||
JSONzip.log(this.length + " <> " + that.length);
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < this.length; i += 1) {
|
||||
boolean b;
|
||||
if (this.list[i] instanceof Kim) {
|
||||
b = this.list[i].equals(that.list[i]);
|
||||
} else {
|
||||
Object o = this.list[i];
|
||||
Object q = that.list[i];
|
||||
if (o instanceof Number) {
|
||||
o = o.toString();
|
||||
}
|
||||
if (q instanceof Number) {
|
||||
q = q.toString();
|
||||
}
|
||||
b = o.equals(q);
|
||||
}
|
||||
if (!b) {
|
||||
JSONzip.log("\n[" + i + "]\n " + this.list[i] + "\n "
|
||||
+ that.list[i] + "\n " + this.ticks[i] + "\n "
|
||||
+ that.ticks[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a value in the keep. Compact the keep if it is full. The next
|
||||
* time this value is encountered, its integer can be sent instead.
|
||||
* @param value A value.
|
||||
*/
|
||||
public void register(Object value) {
|
||||
if (JSONzip.probe) {
|
||||
int integer = find(value);
|
||||
if (integer >= 0) {
|
||||
JSONzip.log("\nDuplicate key " + value);
|
||||
}
|
||||
}
|
||||
if (this.length >= this.capacity) {
|
||||
compact();
|
||||
}
|
||||
this.list[this.length] = value;
|
||||
this.map.put(value, this.length);
|
||||
this.ticks[this.length] = 1;
|
||||
if (JSONzip.probe) {
|
||||
JSONzip.log("<" + this.length + " " + value + "> ");
|
||||
}
|
||||
this.length += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value associated with the integer.
|
||||
* @param integer The number of an item in the keep.
|
||||
* @return The value.
|
||||
*/
|
||||
abstract public Object value(int integer);
|
||||
public Object value(int integer) {
|
||||
return this.list[integer];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ package org.json.zip;
|
|||
* processors. Testing that JSONzip can compress an object and reproduce a
|
||||
* corresponding object is not sufficient. Complete testing requires that the
|
||||
* same internal data structures were constructed on both ends. If those
|
||||
* structures are not equivalent, then it is likely that the implementations
|
||||
* are not correct, even if convention tests are passed.
|
||||
* structures are not exactly equivalent, then it is likely that the
|
||||
* implementations are not correct, even if conventional tests are passed.
|
||||
*
|
||||
* PostMortem allows for testing of deep structures without breaking
|
||||
* encapsulation.
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package org.json.zip;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -32,27 +30,27 @@ import org.json.Kim;
|
|||
*/
|
||||
|
||||
/**
|
||||
* JSONzip is a compression scheme for JSON text.
|
||||
* JSONzip is a binary compression scheme for JSON text.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2014-04-28
|
||||
* @version 2014-05-03
|
||||
*/
|
||||
|
||||
public class Decompressor extends JSONzip {
|
||||
public class Unzipper extends JSONzip {
|
||||
|
||||
/**
|
||||
* A decompressor reads bits from a BitReader.
|
||||
* A decoder reads bits from a BitReader.
|
||||
*/
|
||||
BitReader bitreader;
|
||||
|
||||
/**
|
||||
* Create a new compressor. It may be used for an entire session or
|
||||
* Create a new unzipper. It may be used for an entire session or
|
||||
* subsession.
|
||||
*
|
||||
* @param bitreader
|
||||
* The bitreader that this decompressor will read from.
|
||||
* The bitreader that this decoder will read from.
|
||||
*/
|
||||
public Decompressor(BitReader bitreader) {
|
||||
public Unzipper(BitReader bitreader) {
|
||||
super();
|
||||
this.bitreader = bitreader;
|
||||
}
|
||||
|
@ -81,9 +79,9 @@ public class Decompressor extends JSONzip {
|
|||
* Read enough bits to obtain an integer from the keep, and increase that
|
||||
* integer's weight.
|
||||
*
|
||||
* @param keep
|
||||
* @param bitreader
|
||||
* @return
|
||||
* @param keep The keep providing the context.
|
||||
* @param bitreader The bitreader that is the source of bits.
|
||||
* @return The value associated with the number.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private Object getAndTick(Keep keep, BitReader bitreader)
|
||||
|
@ -110,13 +108,13 @@ public class Decompressor extends JSONzip {
|
|||
* The pad method skips the bits that padded a stream to fit some
|
||||
* allocation. pad(8) will skip over the remainder of a byte.
|
||||
*
|
||||
* @param factor
|
||||
* @param width The width of the pad field in bits.
|
||||
* @return true if all of the padding bits were zero.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public boolean pad(int factor) throws JSONException {
|
||||
public boolean pad(int width) throws JSONException {
|
||||
try {
|
||||
return this.bitreader.pad(factor);
|
||||
return this.bitreader.pad(width);
|
||||
} catch (Throwable e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
|
@ -142,28 +140,76 @@ public class Decompressor extends JSONzip {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Huffman encoded characters into a keep.
|
||||
* @param huff A Huffman decoder.
|
||||
* @param ext A Huffman decoder for the extended bytes.
|
||||
* @param keep The keep that will receive the kim.
|
||||
* @return The string that was read.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private String read(Huff huff, Huff ext, Keep keep) throws JSONException {
|
||||
Kim kim;
|
||||
int at = 0;
|
||||
int allocation = 256;
|
||||
byte[] bytes = new byte[allocation];
|
||||
if (bit()) {
|
||||
return getAndTick(keep, this.bitreader).toString();
|
||||
}
|
||||
while (true) {
|
||||
if (at >= allocation) {
|
||||
allocation *= 2;
|
||||
bytes = java.util.Arrays.copyOf(bytes, allocation);
|
||||
}
|
||||
int c = huff.read(this.bitreader);
|
||||
if (c == end) {
|
||||
break;
|
||||
}
|
||||
while ((c & 128) == 128) {
|
||||
bytes[at] = (byte) c;
|
||||
at += 1;
|
||||
c = ext.read(this.bitreader);
|
||||
}
|
||||
bytes[at] = (byte) c;
|
||||
at += 1;
|
||||
}
|
||||
if (at == 0) {
|
||||
return "";
|
||||
}
|
||||
kim = new Kim(bytes, at);
|
||||
keep.register(kim);
|
||||
return kim.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a JSONArray.
|
||||
*
|
||||
* @param stringy
|
||||
* true if the first element is a string.
|
||||
* @return
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONArray readArray(boolean stringy) throws JSONException {
|
||||
JSONArray jsonarray = new JSONArray();
|
||||
jsonarray.put(stringy ? readString() : readValue());
|
||||
jsonarray.put(stringy
|
||||
? read(this.stringhuff, this.stringhuffext, this.stringkeep)
|
||||
: readValue());
|
||||
while (true) {
|
||||
if (probe) {
|
||||
log("\n");
|
||||
log();
|
||||
}
|
||||
if (!bit()) {
|
||||
if (!bit()) {
|
||||
return jsonarray;
|
||||
}
|
||||
jsonarray.put(stringy ? readValue() : readString());
|
||||
jsonarray.put(stringy
|
||||
? readValue()
|
||||
: read(this.stringhuff, this.stringhuffext,
|
||||
this.stringkeep));
|
||||
} else {
|
||||
jsonarray.put(stringy ? readString() : readValue());
|
||||
jsonarray.put(stringy
|
||||
? read(this.stringhuff, this.stringhuffext,
|
||||
this.stringkeep)
|
||||
: readValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +217,7 @@ public class Decompressor extends JSONzip {
|
|||
/**
|
||||
* Read a JSON value. The type of value is determined by the next 3 bits.
|
||||
*
|
||||
* @return
|
||||
* @return The read value.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private Object readJSON() throws JSONException {
|
||||
|
@ -195,96 +241,25 @@ public class Decompressor extends JSONzip {
|
|||
}
|
||||
}
|
||||
|
||||
private String readName() throws JSONException {
|
||||
byte[] bytes = new byte[65536];
|
||||
int length = 0;
|
||||
if (!bit()) {
|
||||
while (true) {
|
||||
int c = this.namehuff.read(this.bitreader);
|
||||
if (c == end) {
|
||||
break;
|
||||
}
|
||||
bytes[length] = (byte) c;
|
||||
length += 1;
|
||||
}
|
||||
if (length == 0) {
|
||||
return "";
|
||||
}
|
||||
Kim kim = new Kim(bytes, length);
|
||||
this.namekeep.register(kim);
|
||||
return kim.toString();
|
||||
}
|
||||
return getAndTick(this.namekeep, this.bitreader).toString();
|
||||
}
|
||||
|
||||
private JSONObject readObject() throws JSONException {
|
||||
JSONObject jsonobject = new JSONObject();
|
||||
while (true) {
|
||||
if (probe) {
|
||||
log();
|
||||
}
|
||||
String name = readName();
|
||||
jsonobject.put(name, !bit() ? readString() : readValue());
|
||||
String name = read(this.namehuff, this.namehuffext, this.namekeep);
|
||||
if (jsonobject.opt(name) != null) {
|
||||
throw new JSONException("Duplicate key.");
|
||||
}
|
||||
jsonobject.put(name, !bit()
|
||||
? read(this.stringhuff, this.stringhuffext, this.stringkeep)
|
||||
: readValue());
|
||||
if (!bit()) {
|
||||
return jsonobject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String readString() throws JSONException {
|
||||
Kim kim;
|
||||
int from = 0;
|
||||
int thru = 0;
|
||||
int previousFrom = none;
|
||||
int previousThru = 0;
|
||||
if (bit()) {
|
||||
return getAndTick(this.stringkeep, this.bitreader).toString();
|
||||
}
|
||||
byte[] bytes = new byte[65536];
|
||||
boolean one = bit();
|
||||
this.substringkeep.reserve();
|
||||
while (true) {
|
||||
if (one) {
|
||||
from = thru;
|
||||
kim = (Kim) getAndTick(this.substringkeep, this.bitreader);
|
||||
thru = kim.copy(bytes, from);
|
||||
if (previousFrom != none) {
|
||||
this.substringkeep.registerOne(new Kim(bytes, previousFrom,
|
||||
previousThru + 1));
|
||||
}
|
||||
previousFrom = from;
|
||||
previousThru = thru;
|
||||
one = bit();
|
||||
} else {
|
||||
from = none;
|
||||
while (true) {
|
||||
int c = this.substringhuff.read(this.bitreader);
|
||||
if (c == end) {
|
||||
break;
|
||||
}
|
||||
bytes[thru] = (byte) c;
|
||||
thru += 1;
|
||||
if (previousFrom != none) {
|
||||
this.substringkeep.registerOne(new Kim(bytes,
|
||||
previousFrom, previousThru + 1));
|
||||
previousFrom = none;
|
||||
}
|
||||
}
|
||||
if (!bit()) {
|
||||
break;
|
||||
}
|
||||
one = true;
|
||||
}
|
||||
}
|
||||
if (thru == 0) {
|
||||
return "";
|
||||
}
|
||||
kim = new Kim(bytes, thru);
|
||||
this.stringkeep.register(kim);
|
||||
this.substringkeep.registerMany(kim);
|
||||
return kim.toString();
|
||||
}
|
||||
|
||||
private Object readValue() throws JSONException {
|
||||
switch (read(2)) {
|
||||
case 0:
|
||||
|
@ -298,7 +273,7 @@ public class Decompressor extends JSONzip {
|
|||
integer += int7;
|
||||
break;
|
||||
}
|
||||
return new Integer(integer);
|
||||
return integer;
|
||||
case 1:
|
||||
byte[] bytes = new byte[256];
|
||||
int length = 0;
|
||||
|
@ -314,13 +289,13 @@ public class Decompressor extends JSONzip {
|
|||
try {
|
||||
value = JSONObject.stringToValue(new String(bytes, 0, length,
|
||||
"US-ASCII"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
} catch (java.io.UnsupportedEncodingException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
this.values.register(value);
|
||||
this.valuekeep.register(value);
|
||||
return value;
|
||||
case 2:
|
||||
return getAndTick(this.values, this.bitreader);
|
||||
return getAndTick(this.valuekeep, this.bitreader);
|
||||
case 3:
|
||||
return readJSON();
|
||||
default:
|
||||
|
@ -328,8 +303,8 @@ public class Decompressor extends JSONzip {
|
|||
}
|
||||
}
|
||||
|
||||
public Object unzip() throws JSONException {
|
||||
begin();
|
||||
public Object decode() throws JSONException {
|
||||
generate();
|
||||
return readJSON();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package org.json.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
@ -35,36 +34,36 @@ import org.json.Kim;
|
|||
*/
|
||||
|
||||
/**
|
||||
* JSONzip is a compression scheme for JSON text.
|
||||
* JSONzip is a binary compression scheme for JSON text.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2014-04-28
|
||||
* @version 2014-05-03
|
||||
*/
|
||||
|
||||
/**
|
||||
* A compressor implements the compression behavior of JSONzip. It provides a
|
||||
* An encoder implements the compression behavior of JSONzip. It provides a
|
||||
* zip method that takes a JSONObject or JSONArray and delivers a stream of
|
||||
* bits to a BitWriter.
|
||||
*
|
||||
* FOR EVALUATION PURPOSES ONLY. THIS PACKAGE HAS NOT BEEN TESTED ADEQUATELY
|
||||
* FOR PRODUCTION USE.
|
||||
*/
|
||||
public class Compressor extends JSONzip {
|
||||
public class Zipper extends JSONzip {
|
||||
|
||||
/**
|
||||
* A compressor outputs to a BitWriter.
|
||||
* An encoder outputs to a BitWriter.
|
||||
*/
|
||||
final BitWriter bitwriter;
|
||||
|
||||
/**
|
||||
* Create a new compressor. It may be used for an entire session or
|
||||
* Create a new encoder. It may be used for an entire session or
|
||||
* subsession.
|
||||
*
|
||||
* @param bitwriter
|
||||
* The BitWriter this Compressor will output to. Don't forget to
|
||||
* flush.
|
||||
* The BitWriter this encoder will output to.
|
||||
* Don't forget to flush.
|
||||
*/
|
||||
public Compressor(BitWriter bitwriter) {
|
||||
public Zipper(BitWriter bitwriter) {
|
||||
super();
|
||||
this.bitwriter = bitwriter;
|
||||
}
|
||||
|
@ -76,7 +75,7 @@ public class Compressor extends JSONzip {
|
|||
*
|
||||
* @param digit
|
||||
* An ASCII character from a JSON number.
|
||||
* @return
|
||||
* @return The number code.
|
||||
*/
|
||||
private static int bcd(char digit) {
|
||||
if (digit >= '0' && digit <= '9') {
|
||||
|
@ -107,7 +106,7 @@ public class Compressor extends JSONzip {
|
|||
/**
|
||||
* Output a one bit.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private void one() throws JSONException {
|
||||
write(1, 1);
|
||||
|
@ -116,15 +115,15 @@ public class Compressor extends JSONzip {
|
|||
/**
|
||||
* Pad the output to fill an allotment of bits.
|
||||
*
|
||||
* @param factor
|
||||
* @param width
|
||||
* The size of the bit allotment. A value of 8 will complete and
|
||||
* flush the current byte. If you don't pad, then some of the
|
||||
* last bits might not be sent to the Output Stream.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void pad(int factor) throws JSONException {
|
||||
public void pad(int width) throws JSONException {
|
||||
try {
|
||||
this.bitwriter.pad(factor);
|
||||
this.bitwriter.pad(width);
|
||||
} catch (Throwable e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
|
@ -171,29 +170,19 @@ public class Compressor extends JSONzip {
|
|||
* A kim containing the bytes to be written.
|
||||
* @param huff
|
||||
* The Huffman encoder.
|
||||
* @param ext
|
||||
* The Huffman encoder for the extended bytes.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private void write(Kim kim, Huff huff) throws JSONException {
|
||||
write(kim, 0, kim.length, huff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a range of bytes from a Kim with Huffman encoding.
|
||||
*
|
||||
* @param kim
|
||||
* A Kim containing the bytes to be written.
|
||||
* @param from
|
||||
* The index of the first byte to write.
|
||||
* @param thru
|
||||
* The index after the last byte to write.
|
||||
* @param huff
|
||||
* The Huffman encoder.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private void write(Kim kim, int from, int thru, Huff huff)
|
||||
throws JSONException {
|
||||
for (int at = from; at < thru; at += 1) {
|
||||
write(kim.get(at), huff);
|
||||
private void write(Kim kim, Huff huff, Huff ext) throws JSONException {
|
||||
for (int at = 0; at < kim.length; at += 1) {
|
||||
int c = kim.get(at);
|
||||
write(c, huff);
|
||||
while ((c & 128) == 128) {
|
||||
at += 1;
|
||||
c = kim.get(at);
|
||||
write(c, ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,7 +196,7 @@ public class Compressor extends JSONzip {
|
|||
* The Keep that the integer is one of.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private void writeAndTick(int integer, Keep keep) throws JSONException {
|
||||
private void write(int integer, Keep keep) throws JSONException {
|
||||
int width = keep.bitsize();
|
||||
keep.tick(integer);
|
||||
if (probe) {
|
||||
|
@ -219,10 +208,10 @@ public class Compressor extends JSONzip {
|
|||
/**
|
||||
* Write a JSON Array.
|
||||
*
|
||||
* @param jsonarray
|
||||
* @throws JSONException
|
||||
* @param jsonarray The JSONArray to write.
|
||||
* @throws JSONException If the write fails.
|
||||
*/
|
||||
private void writeArray(JSONArray jsonarray) throws JSONException {
|
||||
private void write(JSONArray jsonarray) throws JSONException {
|
||||
|
||||
// JSONzip has three encodings for arrays:
|
||||
// The array is empty (zipEmptyArray).
|
||||
|
@ -295,9 +284,9 @@ public class Compressor extends JSONzip {
|
|||
value = new JSONArray(value);
|
||||
}
|
||||
if (value instanceof JSONObject) {
|
||||
writeObject((JSONObject) value);
|
||||
write((JSONObject) value);
|
||||
} else if (value instanceof JSONArray) {
|
||||
writeArray((JSONArray) value);
|
||||
write((JSONArray) value);
|
||||
} else {
|
||||
throw new JSONException("Unrecognized object");
|
||||
}
|
||||
|
@ -308,7 +297,7 @@ public class Compressor extends JSONzip {
|
|||
* Write the name of an object property. Names have their own Keep and
|
||||
* Huffman encoder because they are expected to be a more restricted set.
|
||||
*
|
||||
* @param name
|
||||
* @param name The name string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private void writeName(String name) throws JSONException {
|
||||
|
@ -320,13 +309,13 @@ public class Compressor extends JSONzip {
|
|||
int integer = this.namekeep.find(kim);
|
||||
if (integer != none) {
|
||||
one();
|
||||
writeAndTick(integer, this.namekeep);
|
||||
write(integer, this.namekeep);
|
||||
} else {
|
||||
|
||||
// Otherwise, emit the string with Huffman encoding, and register it.
|
||||
|
||||
zero();
|
||||
write(kim, this.namehuff);
|
||||
write(kim, this.namehuff, this.namehuffext);
|
||||
write(end, namehuff);
|
||||
this.namekeep.register(kim);
|
||||
}
|
||||
|
@ -335,17 +324,16 @@ public class Compressor extends JSONzip {
|
|||
/**
|
||||
* Write a JSON object.
|
||||
*
|
||||
* @param jsonobject
|
||||
* @return
|
||||
* @param jsonobject The JSONObject to be written.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private void writeObject(JSONObject jsonobject) throws JSONException {
|
||||
private void write(JSONObject jsonobject) throws JSONException {
|
||||
|
||||
// JSONzip has two encodings for objects: Empty Objects (zipEmptyObject) and
|
||||
// non-empty objects (zipObject).
|
||||
|
||||
boolean first = true;
|
||||
Iterator keys = jsonobject.keys();
|
||||
Iterator<String> keys = jsonobject.keys();
|
||||
while (keys.hasNext()) {
|
||||
if (probe) {
|
||||
log();
|
||||
|
@ -379,7 +367,7 @@ public class Compressor extends JSONzip {
|
|||
/**
|
||||
* Write a string.
|
||||
*
|
||||
* @param string
|
||||
* @param string The string to write.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private void writeString(String string) throws JSONException {
|
||||
|
@ -388,9 +376,7 @@ public class Compressor extends JSONzip {
|
|||
|
||||
if (string.length() == 0) {
|
||||
zero();
|
||||
zero();
|
||||
write(end, this.substringhuff);
|
||||
zero();
|
||||
write(end, this.stringhuff);
|
||||
} else {
|
||||
Kim kim = new Kim(string);
|
||||
|
||||
|
@ -400,92 +386,20 @@ public class Compressor extends JSONzip {
|
|||
int integer = this.stringkeep.find(kim);
|
||||
if (integer != none) {
|
||||
one();
|
||||
writeAndTick(integer, this.stringkeep);
|
||||
write(integer, this.stringkeep);
|
||||
} else {
|
||||
|
||||
// But if it is not found, emit the string's substrings. Register the string
|
||||
// so that the next lookup will succeed.
|
||||
// But if it is not found, emit the string's characters. Register the string
|
||||
// so that a later lookup can succeed.
|
||||
|
||||
writeSubstring(kim);
|
||||
zero();
|
||||
write(kim, this.stringhuff, this.stringhuffext);
|
||||
write(end, this.stringhuff);
|
||||
this.stringkeep.register(kim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a string, attempting to match registered substrings.
|
||||
*
|
||||
* @param kim
|
||||
* @throws JSONException
|
||||
*/
|
||||
private void writeSubstring(Kim kim) throws JSONException {
|
||||
this.substringkeep.reserve();
|
||||
zero();
|
||||
int from = 0;
|
||||
int thru = kim.length;
|
||||
int until = thru - JSONzip.minSubstringLength;
|
||||
int previousFrom = none;
|
||||
int previousThru = 0;
|
||||
|
||||
// Find a substring from the substring keep.
|
||||
|
||||
while (true) {
|
||||
int at;
|
||||
int integer = none;
|
||||
for (at = from; at <= until; at += 1) {
|
||||
integer = this.substringkeep.match(kim, at, thru);
|
||||
if (integer != none) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (integer == none) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If a substring is found, emit any characters that were before the matched
|
||||
// substring. Then emit the substring's integer and loop back to match the
|
||||
// remainder with another substring.
|
||||
|
||||
if (from != at) {
|
||||
zero();
|
||||
write(kim, from, at, this.substringhuff);
|
||||
write(end, this.substringhuff);
|
||||
if (previousFrom != none) {
|
||||
this.substringkeep.registerOne(kim, previousFrom,
|
||||
previousThru);
|
||||
previousFrom = none;
|
||||
}
|
||||
}
|
||||
one();
|
||||
writeAndTick(integer, this.substringkeep);
|
||||
from = at + this.substringkeep.length(integer);
|
||||
if (previousFrom != none) {
|
||||
this.substringkeep.registerOne(kim, previousFrom,
|
||||
previousThru);
|
||||
previousFrom = none;
|
||||
}
|
||||
previousFrom = at;
|
||||
previousThru = from + 1;
|
||||
}
|
||||
|
||||
// If a substring is not found, then emit the remaining characters.
|
||||
|
||||
zero();
|
||||
if (from < thru) {
|
||||
write(kim, from, thru, this.substringhuff);
|
||||
if (previousFrom != none) {
|
||||
this.substringkeep.registerOne(kim, previousFrom, previousThru);
|
||||
}
|
||||
}
|
||||
write(end, this.substringhuff);
|
||||
zero();
|
||||
|
||||
// Register the string's substrings in the trie in hopes of future substring
|
||||
// matching.
|
||||
|
||||
substringkeep.registerMany(kim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a value.
|
||||
*
|
||||
|
@ -496,10 +410,10 @@ public class Compressor extends JSONzip {
|
|||
private void writeValue(Object value) throws JSONException {
|
||||
if (value instanceof Number) {
|
||||
String string = JSONObject.numberToString((Number) value);
|
||||
int integer = this.values.find(string);
|
||||
int integer = this.valuekeep.find(string);
|
||||
if (integer != none) {
|
||||
write(2, 2);
|
||||
writeAndTick(integer, this.values);
|
||||
write(integer, this.valuekeep);
|
||||
return;
|
||||
}
|
||||
if (value instanceof Integer || value instanceof Long) {
|
||||
|
@ -527,7 +441,7 @@ public class Compressor extends JSONzip {
|
|||
write(bcd(string.charAt(i)), 4);
|
||||
}
|
||||
write(endOfNumber, 4);
|
||||
this.values.register(string);
|
||||
this.valuekeep.register(string);
|
||||
} else {
|
||||
write(3, 2);
|
||||
writeJSON(value);
|
||||
|
@ -538,32 +452,30 @@ public class Compressor extends JSONzip {
|
|||
* Output a zero bit.
|
||||
*
|
||||
* @throws JSONException
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void zero() throws JSONException {
|
||||
write(0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress a JSONObject.
|
||||
* Encode a JSONObject.
|
||||
*
|
||||
* @param jsonobject
|
||||
* @param jsonobject The JSONObject.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void zip(JSONObject jsonobject) throws JSONException {
|
||||
begin();
|
||||
public void encode(JSONObject jsonobject) throws JSONException {
|
||||
generate();
|
||||
writeJSON(jsonobject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress a JSONArray.
|
||||
* Encode a JSONArray.
|
||||
*
|
||||
* @param jsonarray
|
||||
* @param jsonarray The JSONArray.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void zip(JSONArray jsonarray) throws JSONException {
|
||||
begin();
|
||||
public void encode(JSONArray jsonarray) throws JSONException {
|
||||
generate();
|
||||
writeJSON(jsonarray);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue