mirror of
https://github.com/ethauvin/JSON-java.git
synced 2025-06-17 07:50:52 -07:00
This package needs a new owner.
This commit is contained in:
parent
8114b976ce
commit
9b6872b6e5
14 changed files with 6 additions and 2472 deletions
372
Kim.java
372
Kim.java
|
@ -1,372 +0,0 @@
|
||||||
package org.json;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2013 JSON.org
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kim makes immutable eight bit Unicode strings. If the MSB of a byte is set,
|
|
||||||
* then the next byte is a continuation byte. The last byte of a character
|
|
||||||
* never has the MSB reset. Every byte that is not the last byte has the MSB
|
|
||||||
* set. Kim stands for "Keep it minimal". A Unicode character is never longer
|
|
||||||
* than 3 bytes. Every byte contributes 7 bits to the character. ASCII is
|
|
||||||
* unmodified.
|
|
||||||
*
|
|
||||||
* Kim UTF-8
|
|
||||||
* one byte U+007F U+007F
|
|
||||||
* two bytes U+3FFF U+07FF
|
|
||||||
* three bytes U+10FFF U+FFFF
|
|
||||||
* four bytes U+10FFFF
|
|
||||||
*
|
|
||||||
* Characters in the ranges U+0800..U+3FFF and U+10000..U+10FFFF will be one
|
|
||||||
* byte smaller when encoded in Kim compared to UTF-8.
|
|
||||||
*
|
|
||||||
* Kim is beneficial when using scripts such as Old South Arabian, Aramaic,
|
|
||||||
* Avestan, Balinese, Batak, Bopomofo, Buginese, Buhid, Carian, Cherokee,
|
|
||||||
* Coptic, Cyrillic, Deseret, Egyptian Hieroglyphs, Ethiopic, Georgian,
|
|
||||||
* Glagolitic, Gothic, Hangul Jamo, Hanunoo, Hiragana, Kanbun, Kaithi,
|
|
||||||
* Kannada, Katakana, Kharoshthi, Khmer, Lao, Lepcha, Limbu, Lycian, Lydian,
|
|
||||||
* Malayalam, Mandaic, Meroitic, Miao, Mongolian, Myanmar, New Tai Lue,
|
|
||||||
* Ol Chiki, Old Turkic, Oriya, Osmanya, Pahlavi, Parthian, Phags-Pa,
|
|
||||||
* Phoenician, Samaritan, Sharada, Sinhala, Sora Sompeng, Tagalog, Tagbanwa,
|
|
||||||
* Takri, Tai Le, Tai Tham, Tamil, Telugu, Thai, Tibetan, Tifinagh, UCAS.
|
|
||||||
*
|
|
||||||
* A kim object can be constructed from an ordinary UTF-16 string, or from a
|
|
||||||
* byte array. A kim object can produce a UTF-16 string.
|
|
||||||
*
|
|
||||||
* As with UTF-8, it is possible to detect character boundaries within a byte
|
|
||||||
* sequence. UTF-8 is one of the world's great inventions. While Kim is more
|
|
||||||
* efficient, it is not clear that it is worth the expense of transition.
|
|
||||||
*
|
|
||||||
* @version 2013-04-18
|
|
||||||
*/
|
|
||||||
public class Kim {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The byte array containing the kim's content.
|
|
||||||
*/
|
|
||||||
private byte[] bytes = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The kim's hashcode, conforming to Java's hashcode conventions.
|
|
||||||
*/
|
|
||||||
private int hashcode = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of bytes in the kim. The number of bytes can be as much as
|
|
||||||
* three times the number of characters.
|
|
||||||
*/
|
|
||||||
public int length = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The memoization of toString().
|
|
||||||
*/
|
|
||||||
private String string = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a kim from a portion of a byte array.
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* A byte array.
|
|
||||||
* @param from
|
|
||||||
* The index of the first byte.
|
|
||||||
* @param thru
|
|
||||||
* The index of the last byte plus one.
|
|
||||||
*/
|
|
||||||
public Kim(byte[] bytes, int from, int thru) {
|
|
||||||
|
|
||||||
// As the bytes are copied into the new kim, a hashcode is computed using a
|
|
||||||
// modified Fletcher code.
|
|
||||||
|
|
||||||
int sum = 1;
|
|
||||||
int value;
|
|
||||||
this.hashcode = 0;
|
|
||||||
this.length = thru - from;
|
|
||||||
if (this.length > 0) {
|
|
||||||
this.bytes = new byte[this.length];
|
|
||||||
for (int at = 0; at < this.length; at += 1) {
|
|
||||||
value = (int) bytes[at + from] & 0xFF;
|
|
||||||
sum += value;
|
|
||||||
this.hashcode += sum;
|
|
||||||
this.bytes[at] = (byte) value;
|
|
||||||
}
|
|
||||||
this.hashcode += sum << 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a kim from a byte array.
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* The byte array.
|
|
||||||
* @param length
|
|
||||||
* The number of bytes.
|
|
||||||
*/
|
|
||||||
public Kim(byte[] bytes, int length) {
|
|
||||||
this(bytes, 0, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a new kim from a substring of an existing kim. The coordinates are
|
|
||||||
* in byte units, not character units.
|
|
||||||
*
|
|
||||||
* @param kim
|
|
||||||
* The source of bytes.
|
|
||||||
* @param from
|
|
||||||
* The point at which to take bytes.
|
|
||||||
* @param thru
|
|
||||||
* The point at which to stop taking bytes.
|
|
||||||
*/
|
|
||||||
public Kim(Kim kim, int from, int thru) {
|
|
||||||
this(kim.bytes, from, thru);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a kim from a string.
|
|
||||||
*
|
|
||||||
* @param string
|
|
||||||
* The string.
|
|
||||||
* @throws JSONException
|
|
||||||
* if surrogate pair mismatch.
|
|
||||||
*/
|
|
||||||
public Kim(String string) throws JSONException {
|
|
||||||
int stringLength = string.length();
|
|
||||||
this.hashcode = 0;
|
|
||||||
this.length = 0;
|
|
||||||
|
|
||||||
// First pass: Determine the length of the kim, allowing for the UTF-16
|
|
||||||
// to UTF-32 conversion, and then the UTF-32 to Kim conversion.
|
|
||||||
|
|
||||||
if (stringLength > 0) {
|
|
||||||
for (int i = 0; i < stringLength; i += 1) {
|
|
||||||
int c = string.charAt(i);
|
|
||||||
if (c <= 0x7F) {
|
|
||||||
this.length += 1;
|
|
||||||
} else if (c <= 0x3FFF) {
|
|
||||||
this.length += 2;
|
|
||||||
} else {
|
|
||||||
if (c >= 0xD800 && c <= 0xDFFF) {
|
|
||||||
i += 1;
|
|
||||||
int d = string.charAt(i);
|
|
||||||
if (c > 0xDBFF || d < 0xDC00 || d > 0xDFFF) {
|
|
||||||
throw new JSONException("Bad UTF16");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.length += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second pass: Allocate a byte array and fill that array with the conversion
|
|
||||||
// while computing the hashcode.
|
|
||||||
|
|
||||||
this.bytes = new byte[length];
|
|
||||||
int at = 0;
|
|
||||||
int b;
|
|
||||||
int sum = 1;
|
|
||||||
for (int i = 0; i < stringLength; i += 1) {
|
|
||||||
int character = string.charAt(i);
|
|
||||||
if (character <= 0x7F) {
|
|
||||||
bytes[at] = (byte) character;
|
|
||||||
sum += character;
|
|
||||||
this.hashcode += sum;
|
|
||||||
at += 1;
|
|
||||||
} else if (character <= 0x3FFF) {
|
|
||||||
b = 0x80 | (character >>> 7);
|
|
||||||
bytes[at] = (byte) b;
|
|
||||||
sum += b;
|
|
||||||
this.hashcode += sum;
|
|
||||||
at += 1;
|
|
||||||
b = character & 0x7F;
|
|
||||||
bytes[at] = (byte) b;
|
|
||||||
sum += b;
|
|
||||||
this.hashcode += sum;
|
|
||||||
at += 1;
|
|
||||||
} else {
|
|
||||||
if (character >= 0xD800 && character <= 0xDBFF) {
|
|
||||||
i += 1;
|
|
||||||
character = (((character & 0x3FF) << 10) | (string
|
|
||||||
.charAt(i) & 0x3FF)) + 65536;
|
|
||||||
}
|
|
||||||
b = 0x80 | (character >>> 14);
|
|
||||||
bytes[at] = (byte) b;
|
|
||||||
sum += b;
|
|
||||||
this.hashcode += sum;
|
|
||||||
at += 1;
|
|
||||||
b = 0x80 | ((character >>> 7) & 0xFF);
|
|
||||||
bytes[at] = (byte) b;
|
|
||||||
sum += b;
|
|
||||||
this.hashcode += sum;
|
|
||||||
at += 1;
|
|
||||||
b = character & 0x7F;
|
|
||||||
bytes[at] = (byte) b;
|
|
||||||
sum += b;
|
|
||||||
this.hashcode += sum;
|
|
||||||
at += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.hashcode += sum << 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the character at the specified index. The index refers to byte
|
|
||||||
* values and ranges from 0 to length - 1. The index of the next character
|
|
||||||
* is at index + Kim.characterSize(kim.characterAt(index)).
|
|
||||||
*
|
|
||||||
* @param at
|
|
||||||
* the index of the char value. The first character is at 0.
|
|
||||||
* @returns a Unicode character between 0 and 0x10FFFF.
|
|
||||||
* @throws JSONException
|
|
||||||
* if at does not point to a valid character.
|
|
||||||
*/
|
|
||||||
public int characterAt(int at) throws JSONException {
|
|
||||||
int c = get(at);
|
|
||||||
if ((c & 0x80) == 0) {
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
int character;
|
|
||||||
int c1 = get(at + 1);
|
|
||||||
if ((c1 & 0x80) == 0) {
|
|
||||||
character = ((c & 0x7F) << 7) | c1;
|
|
||||||
if (character > 0x7F) {
|
|
||||||
return character;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int c2 = get(at + 2);
|
|
||||||
character = ((c & 0x7F) << 14) | ((c1 & 0x7F) << 7) | c2;
|
|
||||||
if ((c2 & 0x80) == 0 && character > 0x3FFF && character <= 0x10FFFF
|
|
||||||
&& (character < 0xD800 || character > 0xDFFF)) {
|
|
||||||
return character;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new JSONException("Bad character at " + at);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of bytes needed to contain the character in Kim
|
|
||||||
* format.
|
|
||||||
*
|
|
||||||
* @param character
|
|
||||||
* a Unicode character between 0 and 0x10FFFF.
|
|
||||||
* @return 1, 2, or 3
|
|
||||||
* @throws JSONException
|
|
||||||
* if the character is not representable in a kim.
|
|
||||||
*/
|
|
||||||
public static int characterSize(int character) throws JSONException {
|
|
||||||
if (character < 0 || character > 0x10FFFF) {
|
|
||||||
throw new JSONException("Bad character " + character);
|
|
||||||
}
|
|
||||||
return character <= 0x7F ? 1 : character <= 0x3FFF ? 2 : 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy the contents of this kim to a byte array.
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* A byte array of sufficient size.
|
|
||||||
* @param at
|
|
||||||
* The position within the byte array to take the byes.
|
|
||||||
* @return The position immediately after the copy.
|
|
||||||
*/
|
|
||||||
public int copy(byte[] bytes, int at) {
|
|
||||||
System.arraycopy(this.bytes, 0, bytes, at, this.length);
|
|
||||||
return at + this.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Two kim objects containing exactly the same bytes in the same order are
|
|
||||||
* equal to each other.
|
|
||||||
*
|
|
||||||
* @param obj
|
|
||||||
* the other kim with which to compare.
|
|
||||||
* @returns true if this and obj are both kim objects containing identical
|
|
||||||
* byte sequences.
|
|
||||||
*/
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (!(obj instanceof Kim)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Kim that = (Kim) obj;
|
|
||||||
if (this == that) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (this.hashcode != that.hashcode) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return java.util.Arrays.equals(this.bytes, that.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a byte from a kim.
|
|
||||||
* @param at
|
|
||||||
* The position of the byte. The first byte is at 0.
|
|
||||||
* @return The byte.
|
|
||||||
* @throws JSONException
|
|
||||||
* if there is no byte at that position.
|
|
||||||
*/
|
|
||||||
public int get(int at) throws JSONException {
|
|
||||||
if (at < 0 || at > this.length) {
|
|
||||||
throw new JSONException("Bad character at " + at);
|
|
||||||
}
|
|
||||||
return ((int) this.bytes[at]) & 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a hash code value for the kim.
|
|
||||||
*/
|
|
||||||
public int hashCode() {
|
|
||||||
return this.hashcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produce a UTF-16 String from this kim. The number of codepoints in the
|
|
||||||
* string will not be greater than the number of bytes in the kim, although
|
|
||||||
* it could be less.
|
|
||||||
*
|
|
||||||
* @return The string. A kim memoizes its string representation.
|
|
||||||
* @throws JSONException
|
|
||||||
* if the kim is not valid.
|
|
||||||
*/
|
|
||||||
public String toString() throws JSONException {
|
|
||||||
if (this.string == null) {
|
|
||||||
int c;
|
|
||||||
int length = 0;
|
|
||||||
char chars[] = new char[this.length];
|
|
||||||
for (int at = 0; at < this.length; at += characterSize(c)) {
|
|
||||||
c = this.characterAt(at);
|
|
||||||
if (c < 0x10000) {
|
|
||||||
chars[length] = (char) c;
|
|
||||||
length += 1;
|
|
||||||
} else {
|
|
||||||
chars[length] = (char) (0xD800 | ((c - 0x10000) >>> 10));
|
|
||||||
length += 1;
|
|
||||||
chars[length] = (char) (0xDC00 | (c & 0x03FF));
|
|
||||||
length += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.string = new String(chars, 0, length);
|
|
||||||
}
|
|
||||||
return this.string;
|
|
||||||
}
|
|
||||||
}
|
|
7
README
7
README
|
@ -1,9 +1,14 @@
|
||||||
JSON in Java [package org.json]
|
JSON in Java [package org.json]
|
||||||
|
|
||||||
|
This package needs a new owner. I have not used it in over a decade, and I do
|
||||||
|
not have time to maintain programs that I do not use.
|
||||||
|
|
||||||
|
If you think you can give this package a good home, please contact me.
|
||||||
|
|
||||||
Douglas Crockford
|
Douglas Crockford
|
||||||
douglas@crockford.com
|
douglas@crockford.com
|
||||||
|
|
||||||
2011-02-02
|
2015-02-06
|
||||||
|
|
||||||
|
|
||||||
JSON is a light-weight, language independent, data interchange format.
|
JSON is a light-weight, language independent, data interchange format.
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2013 JSON.org
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a big endian bit reader. It reads its bits from an InputStream.
|
|
||||||
*
|
|
||||||
* @version 2013-05-03
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class BitInputStream implements BitReader {
|
|
||||||
/**
|
|
||||||
* The number of bits remaining in the current byte.
|
|
||||||
*/
|
|
||||||
private int available = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Up to a byte's worth of unread bits.
|
|
||||||
*/
|
|
||||||
private int unread = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source of the bits.
|
|
||||||
*/
|
|
||||||
private InputStream in;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of bits read so far. This is used in padding.
|
|
||||||
*/
|
|
||||||
private long nrBits = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a BitReader from an InputStream. The BitReader will take bytes from
|
|
||||||
* the InputStream and unpack them into bits.
|
|
||||||
*
|
|
||||||
* @param in
|
|
||||||
* An InputStream.
|
|
||||||
*/
|
|
||||||
public BitInputStream(InputStream in) {
|
|
||||||
this.in = in;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read one bit.
|
|
||||||
*
|
|
||||||
* @return true if it is a 1 bit.
|
|
||||||
*/
|
|
||||||
public boolean bit() throws IOException {
|
|
||||||
return read(1) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of bits that have been read from this BitInputStream.
|
|
||||||
* This includes pad bits that have been skipped, but might not include
|
|
||||||
* bytes that have been read from the underlying InputStream that have not
|
|
||||||
* yet been delivered as bits.
|
|
||||||
*
|
|
||||||
* @return The number of bits read so far.
|
|
||||||
*/
|
|
||||||
public long nrBits() {
|
|
||||||
return this.nrBits;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check that the rest of the block has been padded with zeroes.
|
|
||||||
*
|
|
||||||
* @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 width) throws IOException {
|
|
||||||
boolean result = true;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read some bits.
|
|
||||||
*
|
|
||||||
* @param width
|
|
||||||
* The number of bits to read. (0..32)
|
|
||||||
* @throws IOException
|
|
||||||
* @return the bits
|
|
||||||
*/
|
|
||||||
public int read(int width) throws IOException {
|
|
||||||
if (width == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (width < 0 || width > 32) {
|
|
||||||
throw new IOException("Bad read width.");
|
|
||||||
}
|
|
||||||
int result = 0;
|
|
||||||
while (width > 0) {
|
|
||||||
if (this.available == 0) {
|
|
||||||
this.unread = this.in.read();
|
|
||||||
if (this.unread < 0) {
|
|
||||||
throw new IOException("Attempt to read past end.");
|
|
||||||
}
|
|
||||||
this.available = 8;
|
|
||||||
}
|
|
||||||
int take = width;
|
|
||||||
if (take > this.available) {
|
|
||||||
take = this.available;
|
|
||||||
}
|
|
||||||
result |= ((this.unread >>> (this.available - take)) &
|
|
||||||
((1 << take) - 1)) << (width - take);
|
|
||||||
this.nrBits += take;
|
|
||||||
this.available -= take;
|
|
||||||
width -= take;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,154 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2013 JSON.org
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a big endian bit writer. It writes its bits to an OutputStream.
|
|
||||||
*
|
|
||||||
* @version 2013-05-03
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class BitOutputStream implements BitWriter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of bits written.
|
|
||||||
*/
|
|
||||||
private long nrBits = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The destination of the bits.
|
|
||||||
*/
|
|
||||||
private OutputStream out;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holder of bits not yet written.
|
|
||||||
*/
|
|
||||||
private int unwritten;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of unused bits in this.unwritten.
|
|
||||||
*/
|
|
||||||
private int vacant = 8;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use an OutputStream to produce a BitWriter. The BitWriter will send its
|
|
||||||
* bits to the OutputStream as each byte is filled.
|
|
||||||
*
|
|
||||||
* @param out
|
|
||||||
* An Output Stream
|
|
||||||
*/
|
|
||||||
public BitOutputStream(OutputStream out) {
|
|
||||||
this.out = out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of bits that have been written to this
|
|
||||||
* bitOutputStream. This may include bits that have not yet been written
|
|
||||||
* to the underlying outputStream.
|
|
||||||
*/
|
|
||||||
public long nrBits() {
|
|
||||||
return this.nrBits;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a 1 bit.
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public void one() throws IOException {
|
|
||||||
write(1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pad the rest of the block with zeros and flush. pad(8) flushes the last
|
|
||||||
* unfinished byte. The underlying OutputStream will be flushed.
|
|
||||||
*
|
|
||||||
* @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 width) throws IOException {
|
|
||||||
int gap = (int)this.nrBits % width;
|
|
||||||
if (gap < 0) {
|
|
||||||
gap += width;
|
|
||||||
}
|
|
||||||
if (gap != 0) {
|
|
||||||
int padding = width - gap;
|
|
||||||
while (padding > 0) {
|
|
||||||
this.zero();
|
|
||||||
padding -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write some bits. Up to 32 bits can be written at a time.
|
|
||||||
*
|
|
||||||
* @param bits
|
|
||||||
* The bits to be written.
|
|
||||||
* @param width
|
|
||||||
* The number of bits to write. (0..32)
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public void write(int bits, int width) throws IOException {
|
|
||||||
if (bits == 0 && width == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (width <= 0 || width > 32) {
|
|
||||||
throw new IOException("Bad write width.");
|
|
||||||
}
|
|
||||||
while (width > 0) {
|
|
||||||
int actual = width;
|
|
||||||
if (actual > this.vacant) {
|
|
||||||
actual = this.vacant;
|
|
||||||
}
|
|
||||||
this.unwritten |= ((bits >>> (width - actual)) &
|
|
||||||
((1 << actual) - 1)) << (this.vacant - actual);
|
|
||||||
width -= actual;
|
|
||||||
nrBits += actual;
|
|
||||||
this.vacant -= actual;
|
|
||||||
if (this.vacant == 0) {
|
|
||||||
this.out.write(this.unwritten);
|
|
||||||
this.unwritten = 0;
|
|
||||||
this.vacant = 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a 0 bit.
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public void zero() throws IOException {
|
|
||||||
write(0, 1);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface BitReader {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read one bit.
|
|
||||||
*
|
|
||||||
* @return true if it is a 1 bit.
|
|
||||||
*/
|
|
||||||
public boolean bit() throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of bits that have been read from this bitreader.
|
|
||||||
*
|
|
||||||
* @return The number of bits read so far.
|
|
||||||
*/
|
|
||||||
public long nrBits();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check that the rest of the block has been padded with zeros.
|
|
||||||
*
|
|
||||||
* @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 width) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read some bits.
|
|
||||||
*
|
|
||||||
* @param width
|
|
||||||
* The number of bits to read. (0..32)
|
|
||||||
* @throws IOException
|
|
||||||
* @return the bits
|
|
||||||
*/
|
|
||||||
public int read(int width) throws IOException;
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A bitwriter is a an interface that allows for doing output at the bit level.
|
|
||||||
* Most IO interfaces only allow for writing at the byte level or higher.
|
|
||||||
*/
|
|
||||||
public interface BitWriter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a 1 bit.
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public void one() throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pad the rest of the block with zeros and flush.
|
|
||||||
*
|
|
||||||
* @param width
|
|
||||||
* The size in bits of the block to pad. This will typically be
|
|
||||||
* 8, 16, 32, 64, 128, 256, etc.
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public void pad(int width) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write some bits. Up to 32 bits can be written at a time.
|
|
||||||
*
|
|
||||||
* @param bits
|
|
||||||
* The bits to be written.
|
|
||||||
* @param width
|
|
||||||
* The number of bits to write. (0..32)
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public void write(int bits, int width) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a 0 bit.
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public void zero() throws IOException;
|
|
||||||
}
|
|
404
zip/Huff.java
404
zip/Huff.java
|
@ -1,404 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2013 JSON.org
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSONzip is a compression scheme for JSON text.
|
|
||||||
* @author JSON.org
|
|
||||||
* @version 2014-05-20
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Huffman encoder/decoder. It operates over a domain of integers, which may
|
|
||||||
* map to characters or other symbols. Symbols that are used frequently are
|
|
||||||
* given shorter codes than symbols that are used infrequently. This usually
|
|
||||||
* produces shorter messages.
|
|
||||||
*
|
|
||||||
* Initially, all of the symbols are given the same weight. The weight of a
|
|
||||||
* 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 {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of symbols known to the encoder.
|
|
||||||
*/
|
|
||||||
private final int domain;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of characters to process before generation is no longer done.
|
|
||||||
*/
|
|
||||||
public static final int education = 1000000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array that maps symbol values to symbols.
|
|
||||||
*/
|
|
||||||
private final Symbol[] symbols;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root of the decoding table, and the terminal of the encoding table.
|
|
||||||
*/
|
|
||||||
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?
|
|
||||||
*/
|
|
||||||
private boolean upToDate = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of bits in the last symbol. This is used in tracing.
|
|
||||||
*/
|
|
||||||
private int width;
|
|
||||||
|
|
||||||
private static class Symbol implements PostMortem {
|
|
||||||
public Symbol back;
|
|
||||||
public Symbol next;
|
|
||||||
public Symbol zero;
|
|
||||||
public Symbol one;
|
|
||||||
public final int integer;
|
|
||||||
public long weight;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a symbol representing a character or other value.
|
|
||||||
*
|
|
||||||
* @param integer
|
|
||||||
* The symbol's number
|
|
||||||
*/
|
|
||||||
public Symbol(int integer) {
|
|
||||||
this.integer = integer;
|
|
||||||
this.weight = 0;
|
|
||||||
this.next = null;
|
|
||||||
this.back = null;
|
|
||||||
this.one = null;
|
|
||||||
this.zero = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean postMortem(PostMortem pm) {
|
|
||||||
boolean result = true;
|
|
||||||
Symbol that = (Symbol) pm;
|
|
||||||
|
|
||||||
if (this.integer != that.integer || this.weight != that.weight) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((this.back == null) != (that.back == null)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Symbol zero = this.zero;
|
|
||||||
Symbol one = this.one;
|
|
||||||
if (zero == null) {
|
|
||||||
if (that.zero != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = zero.postMortem(that.zero);
|
|
||||||
}
|
|
||||||
if (one == null) {
|
|
||||||
if (that.one != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = one.postMortem(that.one);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a Huffman encoder/decoder.
|
|
||||||
*
|
|
||||||
* @param domain
|
|
||||||
* The number of values known to the object.
|
|
||||||
*/
|
|
||||||
public Huff(int domain) {
|
|
||||||
this.domain = domain;
|
|
||||||
this.toLearn = education;
|
|
||||||
int length = domain * 2 - 1;
|
|
||||||
this.symbols = new Symbol[length];
|
|
||||||
|
|
||||||
// Make the leaf symbols.
|
|
||||||
|
|
||||||
for (int i = 0; i < domain; i += 1) {
|
|
||||||
symbols[i] = new Symbol(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the links.
|
|
||||||
|
|
||||||
for (int i = domain; i < length; i += 1) {
|
|
||||||
symbols[i] = new Symbol(none);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate the encoding/decoding table. The table determines the bit
|
|
||||||
* sequences used by the read and write methods.
|
|
||||||
*/
|
|
||||||
public void generate() {
|
|
||||||
if (!this.upToDate) {
|
|
||||||
|
|
||||||
// Phase One: Sort the symbols by weight into a linked list.
|
|
||||||
|
|
||||||
Symbol head = this.symbols[0];
|
|
||||||
Symbol next;
|
|
||||||
Symbol previous = head;
|
|
||||||
Symbol symbol;
|
|
||||||
|
|
||||||
this.table = null;
|
|
||||||
head.next = null;
|
|
||||||
for (int i = 1; i < this.domain; i += 1) {
|
|
||||||
symbol = symbols[i];
|
|
||||||
|
|
||||||
// If this symbol weights less than the head, then it becomes the new head.
|
|
||||||
|
|
||||||
if (symbol.weight < head.weight) {
|
|
||||||
symbol.next = head;
|
|
||||||
head = symbol;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find a connected pair (previous and next) where the symbol weighs the same
|
|
||||||
// or more than previous but less than the next. Link the symbol between them.
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
next = previous.next;
|
|
||||||
if (next == null || symbol.weight < next.weight) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
previous = next;
|
|
||||||
}
|
|
||||||
symbol.next = next;
|
|
||||||
previous.next = symbol;
|
|
||||||
previous = symbol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase Two: Make new symbols from the two lightest symbols until only one
|
|
||||||
// symbol remains. The final symbol becomes the root of the table binary tree.
|
|
||||||
|
|
||||||
int avail = this.domain;
|
|
||||||
Symbol first;
|
|
||||||
Symbol second;
|
|
||||||
previous = head;
|
|
||||||
while (true) {
|
|
||||||
first = head;
|
|
||||||
second = first.next;
|
|
||||||
head = second.next;
|
|
||||||
symbol = this.symbols[avail];
|
|
||||||
avail += 1;
|
|
||||||
symbol.weight = first.weight + second.weight;
|
|
||||||
symbol.zero = first;
|
|
||||||
symbol.one = second;
|
|
||||||
symbol.back = null;
|
|
||||||
first.back = symbol;
|
|
||||||
second.back = symbol;
|
|
||||||
if (head == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the new symbol back into the sorted list.
|
|
||||||
|
|
||||||
if (symbol.weight < head.weight) {
|
|
||||||
symbol.next = head;
|
|
||||||
head = symbol;
|
|
||||||
previous = head;
|
|
||||||
} else {
|
|
||||||
while (true) {
|
|
||||||
next = previous.next;
|
|
||||||
if (next == null || symbol.weight < next.weight) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
previous = next;
|
|
||||||
}
|
|
||||||
symbol.next = next;
|
|
||||||
previous.next = symbol;
|
|
||||||
previous = symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// The last remaining symbol is the root of the table.
|
|
||||||
|
|
||||||
this.table = symbol;
|
|
||||||
this.upToDate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean postMortem(int integer) {
|
|
||||||
int[] bits = new int[this.domain];
|
|
||||||
Symbol symbol = this.symbols[integer];
|
|
||||||
if (symbol.integer != integer) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int i = 0;
|
|
||||||
while (true) {
|
|
||||||
Symbol back = symbol.back;
|
|
||||||
if (back == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (back.zero == symbol) {
|
|
||||||
bits[i] = 0;
|
|
||||||
} else if (back.one == symbol) {
|
|
||||||
bits[i] = 1;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
symbol = back;
|
|
||||||
}
|
|
||||||
if (symbol != this.table) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.width = 0;
|
|
||||||
symbol = this.table;
|
|
||||||
while (symbol.integer == none) {
|
|
||||||
i -= 1;
|
|
||||||
symbol = bits[i] != 0 ? symbol.one : symbol.zero;
|
|
||||||
}
|
|
||||||
return symbol.integer == integer && i == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare two Huffman tables.
|
|
||||||
*/
|
|
||||||
public boolean postMortem(PostMortem pm) {
|
|
||||||
|
|
||||||
// Go through every integer in the domain, generating its bit sequence, and
|
|
||||||
// then prove that that bit sequence produces the same integer.
|
|
||||||
|
|
||||||
for (int integer = 0; integer < this.domain; integer += 1) {
|
|
||||||
if (!postMortem(integer)) {
|
|
||||||
JSONzip.log("\nBad huff ");
|
|
||||||
JSONzip.logchar(integer, integer);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.table.postMortem(((Huff) pm).table);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read bits until a symbol can be identified. The weight of the read
|
|
||||||
* symbol will be incremented.
|
|
||||||
*
|
|
||||||
* @param bitreader
|
|
||||||
* The source of bits.
|
|
||||||
* @return The integer value of the symbol.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
public int read(BitReader bitreader) throws JSONException {
|
|
||||||
try {
|
|
||||||
this.width = 0;
|
|
||||||
Symbol symbol = this.table;
|
|
||||||
while (symbol.integer == none) {
|
|
||||||
this.width += 1;
|
|
||||||
symbol = bitreader.bit() ? symbol.one : symbol.zero;
|
|
||||||
}
|
|
||||||
tick(symbol.integer);
|
|
||||||
if (JSONzip.probe) {
|
|
||||||
JSONzip.logchar(symbol.integer, this.width);
|
|
||||||
}
|
|
||||||
return symbol.integer;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new JSONException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increase the weight associated with a value by 1.
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
* The number of the symbol to tick
|
|
||||||
*/
|
|
||||||
public void tick(int value) {
|
|
||||||
if (this.toLearn > 0) {
|
|
||||||
this.toLearn -= 1;
|
|
||||||
this.symbols[value].weight += 1;
|
|
||||||
this.upToDate = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recur from a symbol back, emitting bits. We recur before emitting to
|
|
||||||
* make the bits come out in the right order.
|
|
||||||
*
|
|
||||||
* @param symbol
|
|
||||||
* The symbol to write.
|
|
||||||
* @param bitwriter
|
|
||||||
* The bitwriter to write it to.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void write(Symbol symbol, BitWriter bitwriter)
|
|
||||||
throws JSONException {
|
|
||||||
try {
|
|
||||||
Symbol back = symbol.back;
|
|
||||||
if (back != null) {
|
|
||||||
this.width += 1;
|
|
||||||
write(back, bitwriter);
|
|
||||||
if (back.zero == symbol) {
|
|
||||||
bitwriter.zero();
|
|
||||||
} else {
|
|
||||||
bitwriter.one();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new JSONException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the bits corresponding to a symbol. The weight of the symbol will
|
|
||||||
* be incremented.
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
* The number of the symbol to write
|
|
||||||
* @param bitwriter
|
|
||||||
* The destination of the bits.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
public void write(int value, BitWriter bitwriter) throws JSONException {
|
|
||||||
this.width = 0;
|
|
||||||
write(this.symbols[value], bitwriter);
|
|
||||||
tick(value);
|
|
||||||
if (JSONzip.probe) {
|
|
||||||
JSONzip.logchar(value, this.width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
255
zip/JSONzip.java
255
zip/JSONzip.java
|
@ -1,255 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2013 JSON.org
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* 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
|
|
||||||
* substantially reduced. It uses a character encoding called Kim (Keep it
|
|
||||||
* minimal) that is smaller than UTF-8 for most East European, African, and
|
|
||||||
* Asian scripts.
|
|
||||||
*
|
|
||||||
* JSONzip tends to reduce most content by about half. If there is a lot of
|
|
||||||
* recurring information, the reduction can be much more dramatic.
|
|
||||||
*
|
|
||||||
* FOR EVALUATION PURPOSES ONLY. THIS PACKAGE HAS NOT YET BEEN TESTED
|
|
||||||
* ADEQUATELY FOR PRODUCTION USE.
|
|
||||||
*
|
|
||||||
* @author JSON.org
|
|
||||||
* @version 2014-05-20
|
|
||||||
*/
|
|
||||||
public abstract class JSONzip implements None, PostMortem {
|
|
||||||
/**
|
|
||||||
* The characters in JSON numbers can be reduced to 4 bits each.
|
|
||||||
*/
|
|
||||||
public static final byte[] bcd = {
|
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '-', '+', 'E'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The end of string code.
|
|
||||||
*/
|
|
||||||
public static final int end = 256;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The end of number code.
|
|
||||||
*/
|
|
||||||
public static final int endOfNumber = bcd.length;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The first positive integer that cannot be encoded in 4 bits.
|
|
||||||
*/
|
|
||||||
public static final long int4 = 16;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The first positive integer that cannot be encoded in 7 bits.
|
|
||||||
*/
|
|
||||||
public static final long int7 = 144;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The first positive integer that cannot be encoded in 14 bits.
|
|
||||||
*/
|
|
||||||
public static final long int14 = 16528;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The package supports tracing for debugging.
|
|
||||||
*/
|
|
||||||
public static final boolean probe = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value code for an empty object.
|
|
||||||
*/
|
|
||||||
public static final int zipEmptyObject = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value code for an empty array.
|
|
||||||
*/
|
|
||||||
public static final int zipEmptyArray = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value code for true.
|
|
||||||
*/
|
|
||||||
public static final int zipTrue = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value code for false.
|
|
||||||
*/
|
|
||||||
public static final int zipFalse = 3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value code for null.
|
|
||||||
*/
|
|
||||||
public static final int zipNull = 4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value code for a non-empty object.
|
|
||||||
*/
|
|
||||||
public static final int zipObject = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value code for an array with a string as its first element.
|
|
||||||
*/
|
|
||||||
public static final int zipArrayString = 6;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value code for an array with a non-string value as its first element.
|
|
||||||
*/
|
|
||||||
public static final int zipArrayValue = 7;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Huffman encoder for names.
|
|
||||||
*/
|
|
||||||
protected final Huff namehuff;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Huffman encoder for names extended bytes.
|
|
||||||
*/
|
|
||||||
protected final Huff namehuffext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A place to keep the names (keys).
|
|
||||||
*/
|
|
||||||
protected final Keep namekeep;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Huffman encoder for string values.
|
|
||||||
*/
|
|
||||||
protected final Huff stringhuff;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Huffman encoder for string values extended bytes.
|
|
||||||
*/
|
|
||||||
protected final Huff stringhuffext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A place to keep the strings.
|
|
||||||
*/
|
|
||||||
protected final Keep stringkeep;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A place to keep the values.
|
|
||||||
*/
|
|
||||||
protected final Keep valuekeep;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the data structures.
|
|
||||||
*/
|
|
||||||
protected JSONzip() {
|
|
||||||
this.namehuff = new Huff(end + 1);
|
|
||||||
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 generate() {
|
|
||||||
this.namehuff.generate();
|
|
||||||
this.namehuffext.generate();
|
|
||||||
this.stringhuff.generate();
|
|
||||||
this.stringhuffext.generate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write an end-of-line to the console.
|
|
||||||
*/
|
|
||||||
static void log() {
|
|
||||||
log("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write an integer to the console.
|
|
||||||
*
|
|
||||||
* @param integer The integer to write to the log.
|
|
||||||
*/
|
|
||||||
static void log(int integer) {
|
|
||||||
log(integer + " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write two integers, separated by ':' to the console.
|
|
||||||
* The second integer is suppressed if it is 1.
|
|
||||||
*
|
|
||||||
* @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) {
|
|
||||||
log(integer);
|
|
||||||
} else {
|
|
||||||
log(integer + ":" + width + " ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a string to the console.
|
|
||||||
*
|
|
||||||
* @param string The string to be written to the log.
|
|
||||||
*/
|
|
||||||
static void log(String string) {
|
|
||||||
System.out.print(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a character or its code to the console.
|
|
||||||
*
|
|
||||||
* @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 <= '}') {
|
|
||||||
log("'" + (char) integer + "':" + width + " ");
|
|
||||||
} else {
|
|
||||||
log(integer, width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is used for testing the implementation of JSONzip. It is not
|
|
||||||
* suitable for any other purpose. It is used to compare a Compressor and a
|
|
||||||
* Decompressor, verifying that the data structures that were built during
|
|
||||||
* zipping and unzipping were the same.
|
|
||||||
*
|
|
||||||
* @return true if the structures match.
|
|
||||||
*/
|
|
||||||
public boolean postMortem(PostMortem pm) {
|
|
||||||
JSONzip that = (JSONzip) pm;
|
|
||||||
return this.namehuff.postMortem(that.namehuff)
|
|
||||||
&& this.namekeep.postMortem(that.namekeep)
|
|
||||||
&& this.stringkeep.postMortem(that.stringkeep)
|
|
||||||
&& this.stringhuff.postMortem(that.stringhuff)
|
|
||||||
&& this.valuekeep.postMortem(that.valuekeep);
|
|
||||||
}
|
|
||||||
}
|
|
191
zip/Keep.java
191
zip/Keep.java
|
@ -1,191 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import org.json.Kim;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2013 JSON.org
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A keep is a data structure that associates strings (or substrings) with
|
|
||||||
* numbers. This allows the sending of small integers instead of strings.
|
|
||||||
*
|
|
||||||
* @author JSON.org
|
|
||||||
* @version 2013-05-03
|
|
||||||
*/
|
|
||||||
class Keep implements None, PostMortem {
|
|
||||||
private int capacity;
|
|
||||||
protected int length;
|
|
||||||
private Object[] list;
|
|
||||||
private HashMap<Object, Integer> map;
|
|
||||||
private int power;
|
|
||||||
private long[] ticks;
|
|
||||||
|
|
||||||
public Keep(int bits) {
|
|
||||||
this.capacity = 1 << bits;
|
|
||||||
this.length = 0;
|
|
||||||
this.power = 0;
|
|
||||||
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 ticks
|
|
||||||
* The current use count of an item.
|
|
||||||
* @return The new use count for that item.
|
|
||||||
*/
|
|
||||||
public static long age(long ticks) {
|
|
||||||
return ticks >= 32 ? 16 : ticks / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the number of bits required to contain an integer based on the
|
|
||||||
* current length of the keep. As the keep fills up, the number of bits
|
|
||||||
* required to identify one of its items goes up.
|
|
||||||
*/
|
|
||||||
public int bitsize() {
|
|
||||||
while (1 << this.power < this.length) {
|
|
||||||
this.power += 1;
|
|
||||||
}
|
|
||||||
return this.power;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increase the usage count on an integer value.
|
|
||||||
*/
|
|
||||||
public void tick(int integer) {
|
|
||||||
this.ticks[integer] += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
public Object value(int integer) {
|
|
||||||
return this.list[integer];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* None is an interface that makes the constant <i>none</i> (short for
|
|
||||||
* negative one or long for -1) available to any class that implements it.
|
|
||||||
* The none value is used to stand for an integer that is not an integer,
|
|
||||||
* such as the negative result of a search.
|
|
||||||
*/
|
|
||||||
public interface None {
|
|
||||||
/**
|
|
||||||
* Negative One.
|
|
||||||
*/
|
|
||||||
public static final int none = -1;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2013 JSON.org
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The PostMortem interface allows for testing the internal state of JSONzip
|
|
||||||
* 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 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.
|
|
||||||
*/
|
|
||||||
public interface PostMortem {
|
|
||||||
/**
|
|
||||||
* Determine if two objects are equivalent.
|
|
||||||
*
|
|
||||||
* @param pm
|
|
||||||
* Another object of the same type.
|
|
||||||
* @return true if they match.
|
|
||||||
*/
|
|
||||||
public boolean postMortem(PostMortem pm);
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
FOR EVALUATION PURPOSES ONLY. THIS PACKAGE HAS NOT BEEN TESTED ADEQUATELY FOR
|
|
||||||
PRODUCTION USE.
|
|
|
@ -1,310 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.json.Kim;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2012 JSON.org
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSONzip is a binary compression scheme for JSON text.
|
|
||||||
*
|
|
||||||
* @author JSON.org
|
|
||||||
* @version 2014-05-03
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class Unzipper extends JSONzip {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A decoder reads bits from a BitReader.
|
|
||||||
*/
|
|
||||||
BitReader bitreader;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new unzipper. It may be used for an entire session or
|
|
||||||
* subsession.
|
|
||||||
*
|
|
||||||
* @param bitreader
|
|
||||||
* The bitreader that this decoder will read from.
|
|
||||||
*/
|
|
||||||
public Unzipper(BitReader bitreader) {
|
|
||||||
super();
|
|
||||||
this.bitreader = bitreader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read one bit.
|
|
||||||
*
|
|
||||||
* @return true if 1, false if 0.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private boolean bit() throws JSONException {
|
|
||||||
boolean value;
|
|
||||||
try {
|
|
||||||
value = this.bitreader.bit();
|
|
||||||
if (probe) {
|
|
||||||
log(value ? 1 : 0);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new JSONException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read enough bits to obtain an integer from the keep, and increase that
|
|
||||||
* integer's weight.
|
|
||||||
*
|
|
||||||
* @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)
|
|
||||||
throws JSONException {
|
|
||||||
try {
|
|
||||||
int width = keep.bitsize();
|
|
||||||
int integer = bitreader.read(width);
|
|
||||||
Object value = keep.value(integer);
|
|
||||||
if (JSONzip.probe) {
|
|
||||||
JSONzip.log("\"" + value + "\"");
|
|
||||||
JSONzip.log(integer, width);
|
|
||||||
}
|
|
||||||
if (integer >= keep.length) {
|
|
||||||
throw new JSONException("Deep error.");
|
|
||||||
}
|
|
||||||
keep.tick(integer);
|
|
||||||
return value;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new JSONException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 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 width) throws JSONException {
|
|
||||||
try {
|
|
||||||
return this.bitreader.pad(width);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new JSONException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read an integer, specifying its width in bits.
|
|
||||||
*
|
|
||||||
* @param width
|
|
||||||
* 0 to 32.
|
|
||||||
* @return An unsigned integer.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private int read(int width) throws JSONException {
|
|
||||||
try {
|
|
||||||
int value = this.bitreader.read(width);
|
|
||||||
if (probe) {
|
|
||||||
log(value, width);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new JSONException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private JSONArray readArray(boolean stringy) throws JSONException {
|
|
||||||
JSONArray jsonarray = new JSONArray();
|
|
||||||
jsonarray.put(stringy
|
|
||||||
? read(this.stringhuff, this.stringhuffext, this.stringkeep)
|
|
||||||
: readValue());
|
|
||||||
while (true) {
|
|
||||||
if (probe) {
|
|
||||||
log();
|
|
||||||
}
|
|
||||||
if (!bit()) {
|
|
||||||
if (!bit()) {
|
|
||||||
return jsonarray;
|
|
||||||
}
|
|
||||||
jsonarray.put(stringy
|
|
||||||
? readValue()
|
|
||||||
: read(this.stringhuff, this.stringhuffext,
|
|
||||||
this.stringkeep));
|
|
||||||
} else {
|
|
||||||
jsonarray.put(stringy
|
|
||||||
? read(this.stringhuff, this.stringhuffext,
|
|
||||||
this.stringkeep)
|
|
||||||
: readValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a JSON value. The type of value is determined by the next 3 bits.
|
|
||||||
*
|
|
||||||
* @return The read value.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private Object readJSON() throws JSONException {
|
|
||||||
switch (read(3)) {
|
|
||||||
case zipObject:
|
|
||||||
return readObject();
|
|
||||||
case zipArrayString:
|
|
||||||
return readArray(true);
|
|
||||||
case zipArrayValue:
|
|
||||||
return readArray(false);
|
|
||||||
case zipEmptyObject:
|
|
||||||
return new JSONObject();
|
|
||||||
case zipEmptyArray:
|
|
||||||
return new JSONArray();
|
|
||||||
case zipTrue:
|
|
||||||
return Boolean.TRUE;
|
|
||||||
case zipFalse:
|
|
||||||
return Boolean.FALSE;
|
|
||||||
default:
|
|
||||||
return JSONObject.NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JSONObject readObject() throws JSONException {
|
|
||||||
JSONObject jsonobject = new JSONObject();
|
|
||||||
while (true) {
|
|
||||||
if (probe) {
|
|
||||||
log();
|
|
||||||
}
|
|
||||||
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 Object readValue() throws JSONException {
|
|
||||||
switch (read(2)) {
|
|
||||||
case 0:
|
|
||||||
int nr_bits = !bit() ? 4 : !bit() ? 7 : 14;
|
|
||||||
int integer = read(nr_bits);
|
|
||||||
switch (nr_bits) {
|
|
||||||
case 7:
|
|
||||||
integer += int4;
|
|
||||||
break;
|
|
||||||
case 14:
|
|
||||||
integer += int7;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return integer;
|
|
||||||
case 1:
|
|
||||||
byte[] bytes = new byte[256];
|
|
||||||
int length = 0;
|
|
||||||
while (true) {
|
|
||||||
int c = read(4);
|
|
||||||
if (c == endOfNumber) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
bytes[length] = bcd[c];
|
|
||||||
length += 1;
|
|
||||||
}
|
|
||||||
Object value;
|
|
||||||
try {
|
|
||||||
value = JSONObject.stringToValue(new String(bytes, 0, length,
|
|
||||||
"US-ASCII"));
|
|
||||||
} catch (java.io.UnsupportedEncodingException e) {
|
|
||||||
throw new JSONException(e);
|
|
||||||
}
|
|
||||||
this.valuekeep.register(value);
|
|
||||||
return value;
|
|
||||||
case 2:
|
|
||||||
return getAndTick(this.valuekeep, this.bitreader);
|
|
||||||
case 3:
|
|
||||||
return readJSON();
|
|
||||||
default:
|
|
||||||
throw new JSONException("Impossible.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object decode() throws JSONException {
|
|
||||||
generate();
|
|
||||||
return readJSON();
|
|
||||||
}
|
|
||||||
}
|
|
481
zip/Zipper.java
481
zip/Zipper.java
|
@ -1,481 +0,0 @@
|
||||||
package org.json.zip;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.json.Kim;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2013 JSON.org
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSONzip is a binary compression scheme for JSON text.
|
|
||||||
*
|
|
||||||
* @author JSON.org
|
|
||||||
* @version 2014-05-03
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 Zipper extends JSONzip {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An encoder outputs to a BitWriter.
|
|
||||||
*/
|
|
||||||
final BitWriter bitwriter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new encoder. It may be used for an entire session or
|
|
||||||
* subsession.
|
|
||||||
*
|
|
||||||
* @param bitwriter
|
|
||||||
* The BitWriter this encoder will output to.
|
|
||||||
* Don't forget to flush.
|
|
||||||
*/
|
|
||||||
public Zipper(BitWriter bitwriter) {
|
|
||||||
super();
|
|
||||||
this.bitwriter = bitwriter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a 4 bit code for a character in a JSON number. The digits '0' to
|
|
||||||
* '9' get the codes 0 to 9. '.' is 10, '-' is 11, '+' is 12, and 'E' or
|
|
||||||
* 'e' is 13.
|
|
||||||
*
|
|
||||||
* @param digit
|
|
||||||
* An ASCII character from a JSON number.
|
|
||||||
* @return The number code.
|
|
||||||
*/
|
|
||||||
private static int bcd(char digit) {
|
|
||||||
if (digit >= '0' && digit <= '9') {
|
|
||||||
return digit - '0';
|
|
||||||
}
|
|
||||||
switch (digit) {
|
|
||||||
case '.':
|
|
||||||
return 10;
|
|
||||||
case '-':
|
|
||||||
return 11;
|
|
||||||
case '+':
|
|
||||||
return 12;
|
|
||||||
default:
|
|
||||||
return 13;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finish the final byte and flush the bitwriter. This does the same thing
|
|
||||||
* as pad(8).
|
|
||||||
*
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
public void flush() throws JSONException {
|
|
||||||
pad(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output a one bit.
|
|
||||||
*
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void one() throws JSONException {
|
|
||||||
write(1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pad the output to fill an allotment of bits.
|
|
||||||
*
|
|
||||||
* @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 width) throws JSONException {
|
|
||||||
try {
|
|
||||||
this.bitwriter.pad(width);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new JSONException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a number, using the number of bits necessary to hold the number.
|
|
||||||
*
|
|
||||||
* @param integer
|
|
||||||
* The value to be encoded.
|
|
||||||
* @param width
|
|
||||||
* The number of bits to encode the value, between 0 and 32.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void write(int integer, int width) throws JSONException {
|
|
||||||
try {
|
|
||||||
this.bitwriter.write(integer, width);
|
|
||||||
if (probe) {
|
|
||||||
log(integer, width);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new JSONException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write an integer with Huffman encoding. The bit pattern that is written
|
|
||||||
* will be determined by the Huffman encoder.
|
|
||||||
*
|
|
||||||
* @param integer
|
|
||||||
* The value to be written.
|
|
||||||
* @param huff
|
|
||||||
* The Huffman encoder.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void write(int integer, Huff huff) throws JSONException {
|
|
||||||
huff.write(integer, this.bitwriter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write each of the bytes in a kim with Huffman encoding.
|
|
||||||
*
|
|
||||||
* @param kim
|
|
||||||
* 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, 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write an integer, using the number of bits necessary to hold the number
|
|
||||||
* as determined by its keep, and increment its usage count in the keep.
|
|
||||||
*
|
|
||||||
* @param integer
|
|
||||||
* The value to be encoded.
|
|
||||||
* @param keep
|
|
||||||
* The Keep that the integer is one of.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void write(int integer, Keep keep) throws JSONException {
|
|
||||||
int width = keep.bitsize();
|
|
||||||
keep.tick(integer);
|
|
||||||
if (probe) {
|
|
||||||
log("\"" + keep.value(integer) + "\"");
|
|
||||||
}
|
|
||||||
write(integer, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a JSON Array.
|
|
||||||
*
|
|
||||||
* @param jsonarray The JSONArray to write.
|
|
||||||
* @throws JSONException If the write fails.
|
|
||||||
*/
|
|
||||||
private void write(JSONArray jsonarray) throws JSONException {
|
|
||||||
|
|
||||||
// JSONzip has three encodings for arrays:
|
|
||||||
// The array is empty (zipEmptyArray).
|
|
||||||
// First value in the array is a string (zipArrayString).
|
|
||||||
// First value in the array is not a string (zipArrayValue).
|
|
||||||
|
|
||||||
boolean stringy = false;
|
|
||||||
int length = jsonarray.length();
|
|
||||||
if (length == 0) {
|
|
||||||
write(zipEmptyArray, 3);
|
|
||||||
} else {
|
|
||||||
Object value = jsonarray.get(0);
|
|
||||||
if (value == null) {
|
|
||||||
value = JSONObject.NULL;
|
|
||||||
}
|
|
||||||
if (value instanceof String) {
|
|
||||||
stringy = true;
|
|
||||||
write(zipArrayString, 3);
|
|
||||||
writeString((String) value);
|
|
||||||
} else {
|
|
||||||
write(zipArrayValue, 3);
|
|
||||||
writeValue(value);
|
|
||||||
}
|
|
||||||
for (int i = 1; i < length; i += 1) {
|
|
||||||
if (probe) {
|
|
||||||
log();
|
|
||||||
}
|
|
||||||
value = jsonarray.get(i);
|
|
||||||
if (value == null) {
|
|
||||||
value = JSONObject.NULL;
|
|
||||||
}
|
|
||||||
if (value instanceof String != stringy) {
|
|
||||||
zero();
|
|
||||||
}
|
|
||||||
one();
|
|
||||||
if (value instanceof String) {
|
|
||||||
writeString((String) value);
|
|
||||||
} else {
|
|
||||||
writeValue(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zero();
|
|
||||||
zero();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a JSON value.
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
* One of these types: JSONObject, JSONArray (or Map or
|
|
||||||
* Collection or array), Number (or Integer or Long or Double),
|
|
||||||
* or String, or Boolean, or JSONObject.NULL, or null.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void writeJSON(Object value) throws JSONException {
|
|
||||||
if (JSONObject.NULL.equals(value)) {
|
|
||||||
write(zipNull, 3);
|
|
||||||
} else if (Boolean.FALSE.equals(value)) {
|
|
||||||
write(zipFalse, 3);
|
|
||||||
} else if (Boolean.TRUE.equals(value)) {
|
|
||||||
write(zipTrue, 3);
|
|
||||||
} else {
|
|
||||||
if (value instanceof Map) {
|
|
||||||
value = new JSONObject((Map) value);
|
|
||||||
} else if (value instanceof Collection) {
|
|
||||||
value = new JSONArray((Collection) value);
|
|
||||||
} else if (value.getClass().isArray()) {
|
|
||||||
value = new JSONArray(value);
|
|
||||||
}
|
|
||||||
if (value instanceof JSONObject) {
|
|
||||||
write((JSONObject) value);
|
|
||||||
} else if (value instanceof JSONArray) {
|
|
||||||
write((JSONArray) value);
|
|
||||||
} else {
|
|
||||||
throw new JSONException("Unrecognized object");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 The name string.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void writeName(String name) throws JSONException {
|
|
||||||
|
|
||||||
// If this name has already been registered, then emit its integer and
|
|
||||||
// increment its usage count.
|
|
||||||
|
|
||||||
Kim kim = new Kim(name);
|
|
||||||
int integer = this.namekeep.find(kim);
|
|
||||||
if (integer != none) {
|
|
||||||
one();
|
|
||||||
write(integer, this.namekeep);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Otherwise, emit the string with Huffman encoding, and register it.
|
|
||||||
|
|
||||||
zero();
|
|
||||||
write(kim, this.namehuff, this.namehuffext);
|
|
||||||
write(end, namehuff);
|
|
||||||
this.namekeep.register(kim);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a JSON object.
|
|
||||||
*
|
|
||||||
* @param jsonobject The JSONObject to be written.
|
|
||||||
* @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<String> keys = jsonobject.keys();
|
|
||||||
while (keys.hasNext()) {
|
|
||||||
if (probe) {
|
|
||||||
log();
|
|
||||||
}
|
|
||||||
Object key = keys.next();
|
|
||||||
if (key instanceof String) {
|
|
||||||
if (first) {
|
|
||||||
first = false;
|
|
||||||
write(zipObject, 3);
|
|
||||||
} else {
|
|
||||||
one();
|
|
||||||
}
|
|
||||||
writeName((String) key);
|
|
||||||
Object value = jsonobject.get((String) key);
|
|
||||||
if (value instanceof String) {
|
|
||||||
zero();
|
|
||||||
writeString((String) value);
|
|
||||||
} else {
|
|
||||||
one();
|
|
||||||
writeValue(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (first) {
|
|
||||||
write(zipEmptyObject, 3);
|
|
||||||
} else {
|
|
||||||
zero();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a string.
|
|
||||||
*
|
|
||||||
* @param string The string to write.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void writeString(String string) throws JSONException {
|
|
||||||
|
|
||||||
// Special case for empty strings.
|
|
||||||
|
|
||||||
if (string.length() == 0) {
|
|
||||||
zero();
|
|
||||||
write(end, this.stringhuff);
|
|
||||||
} else {
|
|
||||||
Kim kim = new Kim(string);
|
|
||||||
|
|
||||||
// Look for the string in the strings keep. If it is found, emit its
|
|
||||||
// integer and count that as a use.
|
|
||||||
|
|
||||||
int integer = this.stringkeep.find(kim);
|
|
||||||
if (integer != none) {
|
|
||||||
one();
|
|
||||||
write(integer, this.stringkeep);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// But if it is not found, emit the string's characters. Register the string
|
|
||||||
// so that a later lookup can succeed.
|
|
||||||
|
|
||||||
zero();
|
|
||||||
write(kim, this.stringhuff, this.stringhuffext);
|
|
||||||
write(end, this.stringhuff);
|
|
||||||
this.stringkeep.register(kim);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a value.
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
* One of these types: Boolean, Number, etc.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void writeValue(Object value) throws JSONException {
|
|
||||||
if (value instanceof Number) {
|
|
||||||
String string = JSONObject.numberToString((Number) value);
|
|
||||||
int integer = this.valuekeep.find(string);
|
|
||||||
if (integer != none) {
|
|
||||||
write(2, 2);
|
|
||||||
write(integer, this.valuekeep);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value instanceof Integer || value instanceof Long) {
|
|
||||||
long longer = ((Number) value).longValue();
|
|
||||||
if (longer >= 0 && longer < int14) {
|
|
||||||
write(0, 2);
|
|
||||||
if (longer < int4) {
|
|
||||||
zero();
|
|
||||||
write((int) longer, 4);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
one();
|
|
||||||
if (longer < int7) {
|
|
||||||
zero();
|
|
||||||
write((int)(longer - int4), 7);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
one();
|
|
||||||
write((int)(longer - int7), 14);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write(1, 2);
|
|
||||||
for (int i = 0; i < string.length(); i += 1) {
|
|
||||||
write(bcd(string.charAt(i)), 4);
|
|
||||||
}
|
|
||||||
write(endOfNumber, 4);
|
|
||||||
this.valuekeep.register(string);
|
|
||||||
} else {
|
|
||||||
write(3, 2);
|
|
||||||
writeJSON(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output a zero bit.
|
|
||||||
*
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void zero() throws JSONException {
|
|
||||||
write(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a JSONObject.
|
|
||||||
*
|
|
||||||
* @param jsonobject The JSONObject.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
public void encode(JSONObject jsonobject) throws JSONException {
|
|
||||||
generate();
|
|
||||||
writeJSON(jsonobject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a JSONArray.
|
|
||||||
*
|
|
||||||
* @param jsonarray The JSONArray.
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
public void encode(JSONArray jsonarray) throws JSONException {
|
|
||||||
generate();
|
|
||||||
writeJSON(jsonarray);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue