diff --git a/.gitignore b/.gitignore index 894b6fe..2700323 100755 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /bin/ /*.bdf +/*.hbdf diff --git a/README.md b/README.md index 42a5636..fc37d69 100644 --- a/README.md +++ b/README.md @@ -8,26 +8,23 @@ - Creating an object - Arrays - Named lists -- Saving classes -- Implementation details +- Human readable representation +- Special notes ### Overview -Binary Data Format (or BDF) is designed to store data in a tree-like binary structure, -like Notch's NBT format, but also open source and free like JSON. The format is -fast and allows multiple data types, it uses 32-bit integers, so BDF files can -be fast and work well on 32-bit systems, but have a maximum size of 2 GB. -BDF allows human readable serialization to see what is going on for debugging -purposes, but it currently can't parse the human readable serialized string to an object. - +Binary Data Format (BDF) is a statically typed data representation +format. It was made to be free, fast, compact, and seamlessly +convertable between its human readable and binary representations. ### Languages -- Java - C++ +- Java ### Data types +- Undefined - Boolean - Integer - Long @@ -35,20 +32,33 @@ purposes, but it currently can't parse the human readable serialized string to a - Byte - Double - Float +- Boolean Array +- Integer Array +- Long Array +- Short Array +- Byte Array +- Double Array +- Float Array - String - Array - Named List -- Empty ### Creating an object -You will need to create a new object to store any data, use a `BdfObject` instance. -You can input serialized data into `BdfObject` via a `BdfDatabase`. +You will need to generate a BdfObject to serialize anything, +this can be done by first generating a BdfReader, or generating +a new object via an existing BdfObject. ```java -// New BDF object -BdfObject bdf = new BdfObject(); +// Create a reader object +BdfReader reader = new BdfReader(); + +// Get the BdfObject instance +BdfObject bdf = reader.getObject(); + +// Generate another BdfObject instance +BdfObject bdf_new = bdf.newObject(); // Get an integer int v = bdf.getInteger(); @@ -56,6 +66,16 @@ int v = bdf.getInteger(); // Set an integer bdf.setInteger(5); +// Set an integer with an automatic type +bdf.setAutoInt(53); + +// Set a primitive array of ints +int intArray[] = {3, 4, 5, 6}; +bdf.setIntegerArray(intArray); + +// Get a byte array +byte[] byteArray = bdf.getByteArray(); + // Get the type of variable of the object int type = bdf.getType(); @@ -66,243 +86,216 @@ if(type == BdfTypes.INTEGER) } // Serialize the BDF object -byte[] data = bdf.serialize().getBytes(); +byte[] data = bdf->serialize(&data, &data_size); +// Load another reader object from the serialized bytes +BdfReader reader2 = new BdfReader(data); +/* + A reader object can be serialized to the human readable + representation as a string or sent over a stream +*/ +reader2.serializeHumanReadable(System.out, new BdfIndent("\t", "\n")); +String data_hr = reader2.serializeHumanReadable(new BdfIndent("\t", "\n")); -// Load another BDF object with the serialized bytes -BdfObject bdf2 = new BdfObject(new BdfDatabase(data)); - -``` - -A `BdfFileManager` -instance can be used in the same way as a `BdfObject`, but it also needs a String parameter -for the path of the file. The file can be written with `BdfFileManager.saveDatabase()`. -A `BdfFileManager` is an instance of `BdfObject`, a `BdfFileManager` can be casted to -a `BdfObject`. - -```java - -// Open a file -BdfFileManager bdf = new BdfFileManager("file.bdf"); - -// Save the database -bdf.saveDatabase(); - -// The file can be casted to a BdfObject -BdfObject bdf2 = (BdfObject) bdf; - -// Bdf -System.out.println(bdf instanceof BdfObject); // true +// A reader object can be loaded from a human readable object +BdfReader reader3 = new BdfReaderHuman(data_hr); ``` ### Arrays -Arrays can be used to store lists of information, they hold `BdfObject`. -The array is called with `new BdfArray()`. It can hold information, get -the size of the array with `BdfArray.size()`, remove elements with -`BdfArray.remove(index)`, set indexes with `BdfArray.set(index, BdfObject)`, -and add elements with `BdfArray.add(BdfObject)`. Arrays also -have support for Iterators and are an instance of `Iterable`. +Arrays can be used to store chunks of information, they hold instances of +BdfObject. Arrays can also be iterated over just like any other array. ```java -// New BDF Object -BdfObject bdf = new BdfObject(); +BdfReader reader = new BdfReader(); +BdfObject bdf = reader.getObject(); -// New BDF Array -BdfArray array = new BdfArray(); +// Can be created from a bdf object +BdfArray array = bdf.newArray(); -// Size +// Get the length of an array int size = array.size(); -// Remove -array.remove(3); // Could be any number +// Remove any index from an array +array.remove(3); -// Set - Could be any number with any object -array.set(4, BdfObject.withString("A String")); +// Set an object to an index of an array +array.set(4, bdf.newObject().setString("A String")); -// Add - Could be any object -array.add(BdfObject.withByte(53)); +// Add an object to an array +array.add(bdf.newObject().setByte((byte)53)); // Set the array to the bdf object bdf.setArray(array); -// Iterate over an array -for(BdfObject o : array) -{ - -} - ``` ### Named lists -Named lists can be used to store data under strings, +Named lists can be used to store data under ids/strings to be used like variables in a program. A named list -can be created with `new BdfNamedList()` and it -has the ability to set with `BdfNamedList.set(String, BdfObject)`, -remove with `BdfNamedList.remove(String)`, and check -for a key with `BdfNamedList.contains(String)`. It also has -features to get all the keys with `BdfNamedList.getKeys()`. -Named lists also have Iterator support and are an instance of -`Iterable`. +can be created similar to an array. ```java -// New bdf named list -BdfNamedList list = new BdfNamedList(); +BdfReader reader = new BdfReader(); +BdfObject bdf = reader.getObject(); -// Set an element with a value -list.set("key1", BdfObject.withInteger(5)); +// New named list +BdfNamedList list = bdf.newNamedList(); + +// Set an element to the named list +list.set("key1", bdf.newObject().setInteger(5)); + +// Use ids instead of strings for optimisation +// if set/get is being called multiple times +// on the same key. + +int key2 = bdf.getKeyLocation("key2"); +list.set(key2, bdf.newObject().setFloat(42.0F)); // Get an elements value int v = list.get("key1").getInteger(); // Check if an element exists -boolean has_key = list.contains("key1"); +bool has_key = list.contains("key1"); // Get the lists keys -String[] keys = list.getKeys(); +int[] keys = list.getKeys(); // Iterate over the lists keys -for(String key : keys) +for(int key : keys) { - + // Get the keys name + String key_name = bdf.getKeyName(key); } ``` -### Saving classes +### Human readable representation -Classes can be saved with `BdfClassManager` and by -implementing the `IBdfClassManager` interface, -adding 2 functions `BdfClassLoad` and `BdfClassSave`. -`BdfClassLoad` is for checking and loading data from -bdf into the classes variables, while `BdfClassSave` -is for packing pre-existing variables into bdf format. -A BdfClassManager can be used to pass the `IBdfClassManager` -interface into. +A big part of binary data format is the human readable +representation. It has a JSON-like syntax. +This can be used with config files and to modify/view +binaries. A big advantage to using the human readable +representation in configuration files is its support +for comments. -A class with `IBdfClassManager` to save the data -could look like this: - -```java - -class HelloWorld implements IBdfClassManager -{ - int iterator = 0; - - @Override - public void BdfClassLoad(BdfObject bdf) - { - // Load scripts here - - // Get the named list - BdfNamedList nl = bdf.getNamedList(); - - // Set the iterator stored in bdf - int iterator = nl.get("iterator").getInteger(); - } - - @Override - public void BdfClassSave(BdfObject bdf) - { - // Save scripts here - - // Create a named list - BdfNamedList nl = new BdfNamedList(); - - // Set the iterator to the named list - nl.set("iterator", BdfObject.withInteger(iterator)); - - // Store the named list - bdf.setNamedList(nl); - } - - public void hello() - { - // Increase the iterator by 1 - iterator++; - - // Say "Hello, World! Script executed times!" - System.out.println("Hello, World! Script executed "+iterator+" times!"); - } - -} - -``` - -A script to manage this could look something like this: - -```java +```hbdf /* - Get a new BdfObject instance, it could be existing, - or from another file, BdfArray, or BdfNamedList instance. + A Named List is represented + by an opening tag and a closing + tag { } */ -BdfObject bdf = new BdfObject(); +{ + /* + A key value pair can be stored + within a Named List with a string + property + */ + "hello": "world", -// Create the HelloWorld class -HelloWorld hello = new HelloWorld(); + /* + Integers can be stored here too. + They have a character at the end + to say what type they are. + + The tag at the end can be: + - I: Integer - a value between -2^31 and 2^31 - 1 + - S: Short - a value between -32768 and 32767 + - L: Long - a value between -2^63 and 2^63 - 1 + - B: Byte - a value between -128 and 127 + - D: Double - has 15 decimal digits of precision + - F: Float - has 7 decimal digits of precision + */ + "number": 42I, + "byte": -23B, + "decimal": 73.5D, -// Get a new BdfClassManager instance to deal with BDF data -BdfClassManager manager = new BdfClassManager(hello); + /* + This is a boolean. It can + be true or false. + */ + "boolTest": false, -// Give the manager an existing BdfObject instance -manager.setBdf(bdf); + /* + Primitive arrays are represented + by a type, an opening tag, and a + closing tag. They are like an array + but they contain only 1 data type. -// Load the classes bdf data -manager.load(); + The tag at the start can be: + - int + - short + - long + - byte + - double + - float + - bool + */ + "intArray": int ( + 64I, 42I, 63I, + 22I, 96I, -12I, + ), -// Call the hello world function -hello.hello(); + /* + The double and float types support + Infinity, -Infinity, and NaN. + + They also support both really + high and really low value numbers. + */ + "doubleArray": double ( + 42.5D, -20D, 400D, + NaND, -InfinityD, InfinityD, + 5.3e-200F, 4e+500F, 2.2e200F, + ) -// Save the classes bdf data -manager.save(); + /* + Arrays are enclosed by an opening + tag and a closing tag [ ] + + Like the Named List, it can hold + any data type. + */ + "people": [ + {"name": "foo", "age": 60B}, + {"name": "bar", "age": 21B}, + ], + + // This is a single-line comment + + /* This is a multi-line comment */ +} ``` -### Implementation details +### Special notes -All integer data types used are signed and Big Endian. +Don't mix bdf types between different +readers, this will cause problems. -**Type (1 byte)** -``` -0: BOOLEAN (1 byte, 0x00 or 0x01) -1: INTEGER (4 bytes) -2: LONG (8 bytes) -3: SHORT (2 bytes) -4: BYTE (1 byte) -5: DOUBLE (8 bytes) -6: FLOAT (4 bytes) +```java -7: STRING -8: ARRAY -9: NAMED_LIST +BdfReader reader1 = new BdfReader(); +BdfReader reader2 = new BdfReader(); -10: EMPTY (0 bytes) +BdfObject bdf1 = reader1.getObject(); +BdfObject bdf2 = reader2.getObject(); + +// Don't do this +bdf1.setNamedList(bdf2.newNamedList()); + +// Or this +bdf1.setArray(bdf2.newArray()); + +// Or this +BdfNamedList nl = bdf1.newArray(); +nl.set("illegal", bdf2.newObject().setString("action")); -11: ARRAY_BOOLEAN -12: ARRAY_INTEGER -13: ARRAY_LONG -14: ARRAY_SHORT -15: ARRAY_BYTE -16: ARRAY_DOUBLE -17: ARRAY_FLOAT ``` -**Object** -- Type (signed byte, 1 byte) -- Payload (Any type, variable length) - -**NamedList** -- Key size (signed int, 4 bytes) -- Key (variable length) -- Payload size (signed int, 4 bytes) -- Payload (Object, variable length) - -**Array** -- Payload size (signed int, 4 bytes) -- Payload (Object, variable length) diff --git a/src/bdf/classes/BdfClassManager.java b/src/bdf/classes/BdfClassManager.java deleted file mode 100755 index 28cff0e..0000000 --- a/src/bdf/classes/BdfClassManager.java +++ /dev/null @@ -1,39 +0,0 @@ -package bdf.classes; - -import bdf.types.BdfObject; - -public class BdfClassManager -{ - protected IBdfClassManager method; - protected BdfObject object = new BdfObject(); - - public BdfClassManager(IBdfClassManager method) - { - // Save some variables for later - this.method = method; - } - - public void setBdf(BdfObject bdf) { - this.object = bdf; - } - - public BdfObject getBdf() { - return this.object; - } - - public void save(BdfObject bdf) { - method.BdfClassSave(bdf); - } - - public void load(BdfObject bdf) { - method.BdfClassLoad(bdf); - } - - public void save() { - this.save(this.object); - } - - public void load() { - this.load(this.object); - } -} diff --git a/src/bdf/classes/IBdfClassManager.java b/src/bdf/classes/IBdfClassManager.java deleted file mode 100755 index 68a0f5d..0000000 --- a/src/bdf/classes/IBdfClassManager.java +++ /dev/null @@ -1,9 +0,0 @@ -package bdf.classes; - -import bdf.types.BdfObject; - -public interface IBdfClassManager -{ - public void BdfClassLoad(BdfObject bdf); - public void BdfClassSave(BdfObject bdf); -} diff --git a/src/bdf/data/BdfDatabase.java b/src/bdf/data/BdfDatabase.java index 650dd02..7648f7a 100644 --- a/src/bdf/data/BdfDatabase.java +++ b/src/bdf/data/BdfDatabase.java @@ -5,16 +5,12 @@ import java.io.OutputStream; public class BdfDatabase implements IBdfDatabase { - private static final int STREAM_CHUNK_SIZE = 1024*1024; + static final int STREAM_CHUNK_SIZE = 1024*1024; - private byte[] database; - private int location; - private int size; + byte[] database; public BdfDatabase(byte[] bytes) { database = bytes; - size = bytes.length; - location = 0; } public BdfDatabase(String str) { @@ -23,28 +19,28 @@ public class BdfDatabase implements IBdfDatabase public BdfDatabase(int size) { this.database = new byte[size]; - this.location = 0; - this.size = size; - } - - private BdfDatabase() { } @Override - public IBdfDatabase getAt(int start, int end) + public IBdfDatabase getCopy(int start, int end) { byte[] database = new byte[end - start]; for(int i=0;i= data.length) { + throw BdfError.createError(BdfError.ERROR_END_OF_FILE, this); + } + + char c = getChar(); + + // Comments + if(c == '/' && offset < data.length) + { + char c2 = getChar(1); + + // Line comment + if(c2 == '/') + { + for(;;) + { + if(offset + 1 >= data.length) { + break; + } + + increment(); + c = getChar(); + + if(c == '\n') { + break; + } + } + } + + // Multi-line comment + else if(c2 == '*') + { + for(;;) + { + if(offset + 1 >= data.length) { + throw BdfError.createError(BdfError.ERROR_UNESCAPED_COMMENT, getPointer(-1)); + } + + increment(); + c = getChar(); + + if(c == '*' && offset < data.length && getChar(1) == '/') { + increment(); + break; + } + } + } + + else { + return; + } + } + + else if(!(c == '\n' || c == '\t' || c == ' ')) { + return; + } + + increment(); + } + } + + // In the format "abc\n\t\u0003..." + public String getQuotedString() + { + if(getChar() != '"') { + throw BdfError.createError(BdfError.ERROR_SYNTAX, this); + } + + increment(); + String str = ""; + + for(;;) + { + if(offset >= data.length) { + throw BdfError.createError(BdfError.ERROR_UNESCAPED_STRING, this); + } + + char c = getChar(); + + // Check for back slashes + if(c == '\\') + { + increment(); + c = getChar(); + + switch(c) + { + case 'n': + str += "\n"; + increment(); + break; + case 't': + str += "\t"; + increment(); + break; + case '"': + str += "\""; + increment(); + break; + case '\\': + str += "\\"; + increment(); + break; + case '\n': + str += "\n"; + increment(); + break; + case 'u': // \u0000 + { + if(offset + 5 >= data.length) { + throw BdfError.createError(BdfError.ERROR_UNESCAPED_STRING, getPointer(1)); + } + + char[] hex = getCharArray(1, 4); + char unicode = (char)0; + int m = 1; + + for(int j=hex.length-1;j>=0;j--) + { + c = hex[j]; + + if(c >= '0' && c <= '9') { + unicode += (char)(m * (c - '0')); + } + + else if(c >= 'a' && c <= 'f') { + unicode += (char)(m * (c - 'a' + 10)); + } + + else { + throw BdfError.createError(BdfError.ERROR_SYNTAX, getPointer(1 + (hex.length-j-1))); + } + + m *= 16; + } + + str += unicode; + increment(5); + + break; + } + default: + str += "\\" + c; + } + } + + else if(c == '\n') { + throw BdfError.createError(BdfError.ERROR_SYNTAX, this); + } + + else if(c == '"') { + increment(); + break; + } + + else { + increment(); + str += c; + } + } + + return str; + } + + public boolean isNext(String check) + { + if(check.length() + offset > data.length) { + return false; + } + + for(int i=0;i= 'A' && c <= 'Z') ? (c + 32) : c); + + if(c != check.charAt(i)) { + return false; + } + } + + increment(check.length()); + + return true; + } + + public boolean isInteger() + { + for(int i=offset;i= '0' && c <= '9') { + continue; + } + + throw BdfError.createError(BdfError.ERROR_SYNTAX, new BdfStringPointer(data, i)); + } + + throw BdfError.createError(BdfError.ERROR_END_OF_FILE, new BdfStringPointer(data, data.length - 1)); + } +} diff --git a/src/bdf/data/IBdfDatabase.java b/src/bdf/data/IBdfDatabase.java index 30cd911..8ca3658 100644 --- a/src/bdf/data/IBdfDatabase.java +++ b/src/bdf/data/IBdfDatabase.java @@ -5,7 +5,7 @@ import java.io.OutputStream; public interface IBdfDatabase { - public IBdfDatabase getAt(int start, int end); + public IBdfDatabase getCopy(int start, int end); public IBdfDatabase getPointer(int location, int size); public IBdfDatabase getPointer(int location); @@ -17,9 +17,18 @@ public interface IBdfDatabase public byte[] getBytes(); public byte[] getBytes(int start, int size); + public byte getByte(); public byte getByte(int i); public String getString(); - public void setBytes(int pos, byte[] bytes); + public void setBytes(byte[] bytes, int offset, int length); + public void setBytes(byte[] bytes, int offset); + public void setBytes(byte[] bytes); + + public void setBytes(IBdfDatabase bytes, int offset, int length); + public void setBytes(IBdfDatabase bytes, int offset); + public void setBytes(IBdfDatabase bytes); + public void setByte(int pos, byte b); + public void setByte(byte b); } diff --git a/src/bdf/file/.gitignore b/src/bdf/file/.gitignore deleted file mode 100644 index 097f6c7..0000000 --- a/src/bdf/file/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/BdfFileManager.class diff --git a/src/bdf/file/BdfFileManager.java b/src/bdf/file/BdfFileManager.java deleted file mode 100644 index 4c46ca9..0000000 --- a/src/bdf/file/BdfFileManager.java +++ /dev/null @@ -1,86 +0,0 @@ -package bdf.file; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.zip.DeflaterOutputStream; - -import bdf.data.BdfDatabase; -import bdf.types.BdfObject; -import bdf.util.FileHelpers; - -public class BdfFileManager extends BdfObject -{ - protected String path; - private boolean compressed; - - private static BdfDatabase init(String path, boolean compressed) - { - // Get the file - File file = new File(path); - - // Does the file have read access - if(file.canRead()) - { - if(compressed) - { - // Return the files contents as a database - return new BdfDatabase(FileHelpers.readAllCompressedIgnoreErrors(path)); - } - - else - { - // Return the files contents as a database - return new BdfDatabase(FileHelpers.readAllIgnoreErrors(path)); - } - } - - // Return an empty database if there is no read access - return new BdfDatabase(0); - } - - public BdfFileManager(String path, boolean compressed) { - super(init(path, compressed)); - this.compressed = compressed; - this.path = path; - } - - public BdfFileManager(String path) { - this(path, false); - } - - public void saveDatabase(String path) - { - try - { - // Get the file handler - File file = new File(path); - - // Create the parent directories - file.getAbsoluteFile().getParentFile().mkdirs(); - - // Get the database file for output - OutputStream out = new FileOutputStream(path); - - if(compressed) { - out = new DeflaterOutputStream(out); - } - - // Write the database to the file - BdfDatabase db = this.serialize(); - db.writeToStream(out); - - // Close the file output stream - out.close(); - } - - catch(IOException e) { - return; - } - } - - public void saveDatabase() { - this.saveDatabase(this.path); - } -} diff --git a/src/bdf/types/BdfArray.java b/src/bdf/types/BdfArray.java index 5ff46e3..ca24133 100644 --- a/src/bdf/types/BdfArray.java +++ b/src/bdf/types/BdfArray.java @@ -5,18 +5,64 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.Iterator; +import bdf.data.BdfStringPointer; import bdf.data.IBdfDatabase; -import bdf.util.DataHelpers; +import bdf.util.BdfError; public class BdfArray implements IBdfType, Iterable { - protected ArrayList elements = new ArrayList(); + protected ArrayList elements; - public BdfArray() { + BdfArray(BdfLookupTable lookupTable, BdfStringPointer ptr) + { + this.elements = new ArrayList(); + + ptr.increment(); + + // [..., ...] + while(true) + { + ptr.ignoreBlanks(); + + if(ptr.getChar() == ']') { + ptr.increment(); + return; + } + + add(new BdfObject(lookupTable, ptr)); + + // There should be a comma after this + ptr.ignoreBlanks(); + + char c = ptr.getChar(); + + if(c == ']') { + ptr.increment(); + return; + } + + if(c != ',') { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + ptr.increment(); + ptr.ignoreBlanks(); + } } - public BdfArray(IBdfDatabase data) + BdfArray(BdfLookupTable lookupTable, int size) { + this.elements = new ArrayList(size); + + for(int i=0;i(); + // Create an iterator value to loop over the data int i = 0; @@ -24,44 +70,42 @@ public class BdfArray implements IBdfType, Iterable while(i < data.size()) { // Get the size of the object - int size = DataHelpers.getByteBuffer(data.getPointer(i, Integer.BYTES)).getInt(); + int size = BdfObject.getSize(data.getPointer(i)); + + if(size <= 0 || i + size > data.size()) { + return; + } // Get the object - BdfObject object = new BdfObject(data.getPointer((i+Integer.BYTES), size)); + BdfObject object = new BdfObject(lookupTable, data.getPointer(i, size)); // Add the object to the elements list elements.add(object); // Increase the iterator by the amount of bytes - i += Integer.BYTES+size; + i += size; } } @Override - public int serializeSeeker() + public int serializeSeeker(int[] locations) { int size = 0; for(BdfObject o : elements) { - size += o.serializeSeeker(); - size += 4; + size += o.serializeSeeker(locations); } return size; } @Override - public int serialize(IBdfDatabase database) + public int serialize(IBdfDatabase database, int[] locations, byte flags) { int pos = 0; - for(BdfObject o : elements) - { - int size = o.serialize(database.getPointer(pos + 4)); - database.setBytes(pos, DataHelpers.serializeInt(size)); - - pos += size; - pos += 4; + for(BdfObject o : elements) { + pos += o.serialize(database.getPointer(pos), locations, (byte)0); } return pos; @@ -119,9 +163,10 @@ public class BdfArray implements IBdfType, Iterable return this; } - public BdfArray remove(int index) { + public BdfObject remove(int index) { + BdfObject bdf = elements.get(index); elements.remove(index); - return this; + return bdf; } public BdfArray remove(BdfObject bdf) { @@ -173,5 +218,12 @@ public class BdfArray implements IBdfType, Iterable } }; } + + @Override + public void getLocationUses(int[] locations) { + for(BdfObject element : elements) { + element.getLocationUses(locations); + } + } } diff --git a/src/bdf/types/BdfLookupTable.java b/src/bdf/types/BdfLookupTable.java new file mode 100644 index 0000000..d599389 --- /dev/null +++ b/src/bdf/types/BdfLookupTable.java @@ -0,0 +1,139 @@ +package bdf.types; + +import java.io.OutputStream; +import java.util.ArrayList; + +import bdf.data.IBdfDatabase; +import bdf.util.DataHelpers; + +class BdfLookupTable implements IBdfType +{ + private ArrayList keys; + private BdfReader reader; + + BdfLookupTable(BdfReader reader) + { + this.keys = new ArrayList(); + this.reader = reader; + } + + BdfLookupTable(BdfReader reader, IBdfDatabase database) + { + this.keys = new ArrayList(); + this.reader = reader; + + for(int i=0;i database.size()) { + return; + } + + keys.add(database.getBytes(i, key_size)); + + i += key_size; + } + } + + int getLocation(byte[] name) + { + for(int i=0;i= 0 && location < keys.size(); + } + + byte[] getName(int location) { + return keys.get(location); + } + + @Override + public int serialize(IBdfDatabase database, int[] locations, byte flags) + { + int upto = 0; + + for(int i=0;i 0) { + locations[i] = next; + next += 1; + } else { + locations[i] = -1; + } + } + + return locations; + } + + @Override + public void serializeHumanReadable(OutputStream stream, BdfIndent indent, int it) { + } + + @Override + public void getLocationUses(int[] locations) { + } + + public int size() { + return keys.size(); + } +} diff --git a/src/bdf/types/BdfNamedList.java b/src/bdf/types/BdfNamedList.java index e0fe021..bc6d3a4 100644 --- a/src/bdf/types/BdfNamedList.java +++ b/src/bdf/types/BdfNamedList.java @@ -2,43 +2,140 @@ package bdf.types; import java.io.IOException; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; +import java.nio.ByteBuffer; import java.util.ArrayList; +import bdf.data.BdfStringPointer; import bdf.data.IBdfDatabase; +import bdf.util.BdfError; import bdf.util.DataHelpers; public class BdfNamedList implements IBdfType { protected class Element { - public byte[] key; + public int key; public BdfObject object; } protected ArrayList elements = new ArrayList(); + protected BdfLookupTable lookupTable; - public BdfNamedList() { + BdfNamedList(BdfLookupTable lookupTable, BdfStringPointer ptr) + { + this.lookupTable = lookupTable; + ptr.increment(); + + // {"key": ..., "key2": ...} + while(true) + { + ptr.ignoreBlanks(); + + char c = ptr.getChar(); + + if(c == '}') { + ptr.increment(); + break; + } + + if(c != '"') { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + String key = ptr.getQuotedString(); + + // There should be a colon after this + ptr.ignoreBlanks(); + if(ptr.getChar() != ':') { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + ptr.increment(); + ptr.ignoreBlanks(); + + set(key, new BdfObject(lookupTable, ptr)); + + // There should be a comma after this + ptr.ignoreBlanks(); + + c = ptr.getChar(); + + if(c == '}') { + ptr.increment(); + return; + } + + if(c != ',') { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + ptr.increment(); + ptr.ignoreBlanks(); + } + } + + BdfNamedList(BdfLookupTable lookupTable) { + this.lookupTable = lookupTable; } - public BdfNamedList(IBdfDatabase data) + BdfNamedList(BdfLookupTable lookupTable, IBdfDatabase data) { + this.lookupTable = lookupTable; + // Create an iterator value to loop over the data int i = 0; // Loop over the data while(i < data.size()) { - // Get the key - int key_size = DataHelpers.getByteBuffer(data.getPointer(i, 4)).getInt(); - i += 4; - byte[] key = data.getPointer(i, key_size).getBytes(); - // Get the object + int key_size = 0; + IBdfDatabase flag_ptr = data.getPointer(i); + int object_size = BdfObject.getSize(flag_ptr); + byte key_size_bytes = BdfObject.getParentFlags(flag_ptr); + + if(object_size <= 0 || i + object_size >= data.size()) { + return; + } + + BdfObject object = new BdfObject(lookupTable, data.getPointer(i, object_size)); + i += object_size; + + switch(key_size_bytes) + { + case 2: + key_size = 1; + break; + case 1: + key_size = 2; + break; + case 0: + key_size = 4; + break; + } + + // Get the key + ByteBuffer key_buff = DataHelpers.getByteBuffer(data.getPointer(i, key_size)); + int key = 0; + + switch(key_size_bytes) + { + case 2: + key = 0xff & key_buff.get(); + break; + case 1: + key = 0xffff & key_buff.getShort(); + break; + case 0: + key = key_buff.getInt(); + break; + } + + if(!lookupTable.hasLocation(key)) { + return; + } + i += key_size; - int object_size = DataHelpers.getByteBuffer(data.getPointer(i, 4)).getInt(); - i += 4; - BdfObject object = new BdfObject(data.getPointer(i, object_size)); // Create a new element and save some data to it Element element = new Element(); @@ -47,48 +144,65 @@ public class BdfNamedList implements IBdfType // Add the object to the elements list elements.add(element); - - // Increase the iterator by the amount of bytes - i += object_size; } } @Override - public int serialize(IBdfDatabase database) + public int serialize(IBdfDatabase database, int[] locations, byte flags) { int pos = 0; for(Element o : elements) { - database.setBytes(pos, DataHelpers.serializeInt(o.key.length)); + int location = locations[o.key]; - pos += 4; + byte size_bytes_tag; + byte size_bytes; - database.setBytes(pos, o.key); + if(location > 65535) { // >= 2 ^ 16 + size_bytes_tag = 0; + size_bytes = 4; + } else if(location > 255) { // >= 2 ^ 8 + size_bytes_tag = 1; + size_bytes = 2; + } else { // < 2 ^ 8 + size_bytes_tag = 2; + size_bytes = 1; + } - pos += o.key.length; + int size = o.object.serialize(database.getPointer(pos), locations, size_bytes_tag); + int offset = pos + size; - int size = o.object.serialize(database.getPointer(pos + 4, database.size() - (pos + 4))); + byte[] bytes = DataHelpers.serializeInt(location); - database.setBytes(pos, DataHelpers.serializeInt(size)); + for(int i=0;i 65535) { // >= 2 ^ 16 + size += 4; + } else if(location > 255) { // >= 2 ^ 8 + size += 2; + } else { // < 2 ^ 8 + size += 1; + } + + size += o.object.serializeSeeker(locations); } return size; @@ -114,7 +228,7 @@ public class BdfNamedList implements IBdfType stream.write(indent.indent.getBytes()); } - stream.write((DataHelpers.serializeString(new String(e.key, StandardCharsets.UTF_8)) + ": ").getBytes()); + stream.write((DataHelpers.serializeString(new String(lookupTable.getName(e.key))) + ": ").getBytes()); e.object.serializeHumanReadable(stream, indent, it + 1); if(elements.size() > i+1) { @@ -131,19 +245,16 @@ public class BdfNamedList implements IBdfType stream.write('}'); } - public BdfObject get(String key) + public BdfObject get(int key) { // Get the object to send back BdfObject object = null; - // Convert the key to bytes - byte[] key_bytes = key.getBytes(); - // Loop over the elements for(Element e : elements) { // Is this the element key - if(DataHelpers.bytesAreEqual(e.key, key_bytes)) + if(e.key == key) { // Set the object object = e.object; @@ -154,7 +265,7 @@ public class BdfNamedList implements IBdfType } // Get a bdf object - BdfObject o = new BdfObject(); + BdfObject o = new BdfObject(lookupTable); // Set the bdf object this.set(key, o); @@ -163,11 +274,8 @@ public class BdfNamedList implements IBdfType return o; } - public BdfNamedList remove(String key) + public BdfObject remove(int key) { - // Convert the key to bytes - byte[] key_bytes = key.getBytes(); - // Loop over the elements for(int i=0;i 1) - { - // Get the type and database values - type = data.getByte(0); - database = data.getPointer(1, data.size() - 1); - - // Set the object variable if there is an object specified - if(type == BdfTypes.STRING) object = database.getString(); - if(type == BdfTypes.ARRAY) object = new BdfArray(database); - if(type == BdfTypes.NAMED_LIST) object = new BdfNamedList(database); - - if(object != null) { - database = null; - } + this.lookupTable = lookupTable; + + // Get the type and database values + int flags = 0xff & data.getByte(0); + type = (byte)(flags % 18); + flags = (byte)((flags - type) / 18); + int size_bytes = getSizeBytes(flags % 3); + + database = data.getPointer(1); + + // Skip the size bytes if size is stored + if(shouldStoreSize(type)) { + database = database.getPointer(size_bytes); } - else - { - // Create a new database + if(database.size() < 0) { database = new BdfDatabase(0); + type = BdfTypes.UNDEFINED; + return; + } + + // Set the object variable if there is an object specified + switch(type) + { + case BdfTypes.STRING: + object = database.getString(); + break; + case BdfTypes.ARRAY: + object = new BdfArray(lookupTable, database); + break; + case BdfTypes.NAMED_LIST: + object = new BdfNamedList(lookupTable, database); + break; + } + + if(object != null) { + database = null; + } + } + + BdfObject(BdfLookupTable lookupTable, BdfStringPointer ptr) + { + this.lookupTable = lookupTable; + + char c = ptr.getChar(); + + if(c == '{') { + setNamedList(new BdfNamedList(lookupTable, ptr)); + return; + } + + if(c == '[') { + setArray(new BdfArray(lookupTable, ptr)); + return; + } + + if(c == '"') { + setString(ptr.getQuotedString()); + return; + } + + boolean isDecimalArray = false; + boolean isPrimitiveArray = false; + byte type = 0; + + if(ptr.isNext("int")) { + type = BdfTypes.ARRAY_INTEGER; + isPrimitiveArray = true; + } + + else if(ptr.isNext("long")) { + type = BdfTypes.ARRAY_LONG; + isPrimitiveArray = true; + } + + else if(ptr.isNext("byte")) { + type = BdfTypes.ARRAY_BYTE; + isPrimitiveArray = true; + } + + else if(ptr.isNext("short")) { + type = BdfTypes.ARRAY_SHORT; + isPrimitiveArray = true; + } + + else if(ptr.isNext("bool")) { + type = BdfTypes.ARRAY_BOOLEAN; + isPrimitiveArray = true; + } + + else if(ptr.isNext("double")) { + type = BdfTypes.ARRAY_DOUBLE; + isPrimitiveArray = true; + isDecimalArray = true; + } + + else if(ptr.isNext("float")) { + type = BdfTypes.ARRAY_FLOAT; + isPrimitiveArray = true; + isDecimalArray = true; + } + + // Deserialize a primitive array + if(isPrimitiveArray) + { + ptr.ignoreBlanks(); + + if(ptr.getChar() != '(') { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + ptr.increment(); + ptr.ignoreBlanks(); + + // Get the size of the array + int size = 0; + + // Get a copy of the pointer + BdfStringPointer ptr2 = ptr.getPointer(0); + + for(;;) + { + if(ptr2.getChar() == ')') { + ptr2.increment(); + break; + } + + if( + ptr2.isNext("true") || ptr2.isNext("false") || + ptr2.isNext("infinityd") || ptr2.isNext("-infinityd") || + ptr2.isNext("infinityf") || ptr2.isNext("-infinityf") || + ptr2.isNext("nand") || ptr2.isNext("nanf")) + { + size += 1; + } + + else + { + for(;;) + { + if(ptr2.getDataLocation() >= ptr2.getDataLength()) { + throw BdfError.createError(BdfError.ERROR_END_OF_FILE, ptr2.getPointer(-1)); + } + + c = ptr2.getChar(); + + if(c >= 'a' && c <= 'z') { + c -= 32; + } + + if(c >= '0' && c <= '9' || ((c == '.' || c == 'E') && isDecimalArray) || c == '+' || c == '-') { + ptr2.increment(); + continue; + } + + if(c == 'B' || c == 'S' || c == 'I' || c == 'L' || c == 'D' || c == 'F') { + ptr2.increment(); + size += 1; + break; + } + + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr2); + } + } + + ptr2.ignoreBlanks(); + + if(ptr2.getChar() == ',') { + ptr2.increment(); + ptr2.ignoreBlanks(); + } + + if(ptr2.getChar() == ')') { + ptr2.increment(); + break; + } + } + + Object array = null; + + switch(type) + { + case BdfTypes.ARRAY_BOOLEAN: + array = new boolean[size]; + break; + case BdfTypes.ARRAY_BYTE: + array = new byte[size]; + break; + case BdfTypes.ARRAY_DOUBLE: + array = new double[size]; + break; + case BdfTypes.ARRAY_FLOAT: + array = new float[size]; + break; + case BdfTypes.ARRAY_INTEGER: + array = new int[size]; + break; + case BdfTypes.ARRAY_LONG: + array = new long[size]; + break; + case BdfTypes.ARRAY_SHORT: + array = new short[size]; + break; + } + + for(int i=0;;i++) + { + if(ptr.getChar() == ')') { + ptr.increment(); + break; + } + + if(ptr.isNext("true")) + { + if(type != BdfTypes.ARRAY_BOOLEAN) { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + boolean[] a = (boolean[]) array; + a[i] = true; + } + + else if(ptr.isNext("false")) + { + if(type != BdfTypes.ARRAY_BOOLEAN) { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + boolean[] a = (boolean[]) array; + a[i] = false; + } + + else if(ptr.isNext("infinityd")) + { + if(type != BdfTypes.ARRAY_DOUBLE) { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + double[] a = (double[]) array; + a[i] = Double.POSITIVE_INFINITY; + } + + else if(ptr.isNext("-infinityd")) + { + if(type != BdfTypes.ARRAY_DOUBLE) { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + double[] a = (double[]) array; + a[i] = Double.NEGATIVE_INFINITY; + } + + else if(ptr.isNext("nand")) + { + if(type != BdfTypes.ARRAY_DOUBLE) { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + double[] a = (double[]) array; + a[i] = Double.NaN; + } + + else if(ptr.isNext("infinityf")) + { + if(type != BdfTypes.ARRAY_FLOAT) { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + float[] a = (float[]) array; + a[i] = Float.POSITIVE_INFINITY; + } + + else if(ptr.isNext("-infinityf")) + { + if(type != BdfTypes.ARRAY_FLOAT) { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + float[] a = (float[]) array; + a[i] = Float.NEGATIVE_INFINITY; + } + + else if(ptr.isNext("nanf")) + { + if(type != BdfTypes.ARRAY_FLOAT) { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + float[] a = (float[]) array; + a[i] = Float.NaN; + } + + else + { + // Parse a number + String number = ""; + + for(;;) + { + c = ptr.getChar(); + + if(c >= 'a' && c <= 'z') { + c -= 32; + } + + if(ptr.getDataLocation() > ptr.getDataLength()) { + throw BdfError.createError(BdfError.ERROR_END_OF_FILE, ptr); + } + + if(c >= '0' && c <= '9' || ((c == '.' || c == 'E') && isDecimalArray) || c == '+' || c == '-') { + ptr.increment(); + number += c; + continue; + } + + switch(c) + { + case 'D': + { + if(type != BdfTypes.ARRAY_DOUBLE) + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + + double[] a = (double[]) array; + a[i] = Double.parseDouble(number); + + ptr.increment(); + break; + } + + case 'F': + { + if(type != BdfTypes.ARRAY_FLOAT) + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + + float[] a = (float[]) array; + a[i] = Float.parseFloat(number); + + ptr.increment(); + break; + } + + case 'I': + { + if(type != BdfTypes.ARRAY_INTEGER) + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + + int[] a = (int[]) array; + a[i] = Integer.parseInt(number); + + ptr.increment(); + break; + } + + case 'L': + { + if(type != BdfTypes.ARRAY_LONG) + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + + long[] a = (long[]) array; + a[i] = Long.parseLong(number); + + ptr.increment(); + break; + } + + case 'S': + { + if(type != BdfTypes.ARRAY_SHORT) + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + + short[] a = (short[]) array; + a[i] = Short.parseShort(number); + + ptr.increment(); + break; + } + + case 'B': + { + if(type != BdfTypes.ARRAY_BYTE) + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + + byte[] a = (byte[]) array; + a[i] = Byte.parseByte(number); + + ptr.increment(); + break; + } + + default: + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + + break; + } + } + + // int (420I, 23I ) + + ptr.ignoreBlanks(); + + if(ptr.getChar() == ',') { + ptr.increment(); + ptr.ignoreBlanks(); + } + + if(ptr.getChar() == ')') { + ptr.increment(); + break; + } + } + + switch(type) + { + case BdfTypes.ARRAY_BOOLEAN: + setBooleanArray((boolean[])array); + break; + case BdfTypes.ARRAY_BYTE: + setByteArray((byte[])array); + break; + case BdfTypes.ARRAY_DOUBLE: + setDoubleArray((double[])array); + break; + case BdfTypes.ARRAY_FLOAT: + setFloatArray((float[])array); + break; + case BdfTypes.ARRAY_INTEGER: + setIntegerArray((int[])array); + break; + case BdfTypes.ARRAY_LONG: + setLongArray((long[])array); + break; + case BdfTypes.ARRAY_SHORT: + setShortArray((short[])array); + break; + } + + return; + } + + if(ptr.isNext("true")) { + setBoolean(true); + return; + } + + if(ptr.isNext("false")) { + setBoolean(false); + return; + } + + if(ptr.isNext("infinityd")) { + setDouble(Double.POSITIVE_INFINITY); + return; + } + + if(ptr.isNext("-infinityd")) { + setDouble(Double.NEGATIVE_INFINITY); + return; + } + + if(ptr.isNext("nand")) { + setDouble(Double.NaN); + return; + } + + if(ptr.isNext("infinityf")) { + setFloat(Float.POSITIVE_INFINITY); + return; + } + + if(ptr.isNext("-infinityf")) { + setFloat(Float.NEGATIVE_INFINITY); + return; + } + + if(ptr.isNext("nanf")) { + setFloat(Float.NaN); + return; + } + + if(ptr.isNext("undefined")) { + database = new BdfDatabase(0); + return; + } + + // Parse a number + String number = ""; + boolean isDecimal = false; + + for(;;) + { + c = ptr.getChar(); + ptr.increment(); + + if(c >= 'a' && c <= 'z') { + c -= 32; + } + + if(ptr.getDataLocation() > ptr.getDataLength()) { + throw BdfError.createError(BdfError.ERROR_END_OF_FILE, ptr); + } + + if(c == '.' || c == 'E') { + isDecimal = true; + number += c; + continue; + } + + if(c >= '0' && c <= '9' || c == '+' || c == '-') { + number += c; + continue; + } + + if(isDecimal && !(c == 'D' || c == 'F')) { + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr.getPointer(-1)); + } + + try + { + switch(c) + { + case 'D': + setDouble(Double.parseDouble(number)); + return; + case 'F': + setFloat(Float.parseFloat(number)); + return; + case 'B': + setByte(Byte.parseByte(number)); + return; + case 'S': + setShort(Short.parseShort(number)); + return; + case 'I': + setInteger(Integer.parseInt(number)); + return; + case 'L': + setLong(Long.parseLong(number)); + return; + default: + throw BdfError.createError(BdfError.ERROR_SYNTAX, ptr); + } + } + + catch(NumberFormatException e) { + throw BdfError.createError(BdfError.ERROR_NUMBER, ptr.getPointer(-number.length())); + } + } + } + + public byte getType() { + return type; + } + + private boolean shouldStoreSize(byte b) { + return b > 7; + } + + static private int getSizeBytes(int size_bytes_tag) + { + switch(size_bytes_tag) + { + case 0: return 4; + case 1: return 2; + case 2: return 1; + default: return 4; + } + } + + static byte getParentFlags(IBdfDatabase db) + { + int flags = 0xff & db.getByte(0); + + byte type = (byte)(flags % 18); + flags = (byte)((flags - type) / 18); + + byte size_bytes = (byte)(flags % 3); + flags = (byte)((flags - size_bytes) / 3); + + byte parent_flags = (byte)(flags % 3); + flags = (byte)((flags - parent_flags) / 3); + + return parent_flags; + } + + static int getSize(IBdfDatabase db) + { + int flags = 0xff & db.getByte(0); + byte type = (byte)(flags % 18); + flags = (byte)((flags - type) / 18); + + int size_bytes = getSizeBytes(flags % 3); + int size = getSize(type); + + if(size != -1) { + return size; + } + + ByteBuffer size_buff = DataHelpers.getByteBuffer(db.getPointer(1, size_bytes)); + + switch(size_bytes) + { + case 4: return size_buff.getInt(); + case 2: return (0xffff & size_buff.getShort()); + case 1: return (0xff & size_buff.get()); + } + + return 0; + } + + static int getSize(byte type) + { + switch(type) + { + case BdfTypes.BOOLEAN: + return 2; + case BdfTypes.BYTE: + return 2; + case BdfTypes.DOUBLE: + return 9; + case BdfTypes.FLOAT: + return 5; + case BdfTypes.INTEGER: + return 5; + case BdfTypes.LONG: + return 9; + case BdfTypes.SHORT: + return 3; + case BdfTypes.UNDEFINED: + return 1; + default: + return -1; } } @Override - public int serialize(IBdfDatabase database) + public void getLocationUses(int[] locations) { - int size; + if(type == BdfTypes.NAMED_LIST || type == BdfTypes.ARRAY) { + ((IBdfType)object).getLocationUses(locations); + } + } + + @Override + public int serialize(IBdfDatabase database, int[] locations, byte parent_flags) + { + int size = last_seek; + boolean storeSize = shouldStoreSize(type); - IBdfDatabase db = database.getPointer(1); + byte size_bytes_tag = 0; + int size_bytes = 0; + + if(storeSize) + { + if(size > 65535) { // >= 2 ^ 16 + size_bytes_tag = 0; + size_bytes = 4; + } else if(size > 255) { // >= 2 ^ 8 + size_bytes_tag = 1; + size_bytes = 2; + } else { // < 2 ^ 8 + size_bytes_tag = 2; + size_bytes = 1; + } + } + + int offset = size_bytes + 1; + byte flags = (byte)(type + (size_bytes_tag * 18) + (parent_flags * 3 * 18)); // Objects switch(type) { case BdfTypes.ARRAY: - size = ((BdfArray)object).serialize(db) + 1; + size = ((BdfArray)object).serialize(database.getPointer(offset), locations, (byte)0) + offset; break; case BdfTypes.NAMED_LIST: - size = ((BdfNamedList)object).serialize(db) + 1; + size = ((BdfNamedList)object).serialize(database.getPointer(offset), locations, (byte)0) + offset; break; case BdfTypes.STRING: - String str = (String)object; - size = str.length() + 1; - db.setBytes(0, str.getBytes()); + byte[] str = ((String)object).getBytes(); + size = str.length + offset; + database.setBytes(str, offset); break; default: - size = this.database.size() + 1; - db.setBytes(0, this.database.getBytes()); + size = this.database.size() + offset; + database.setBytes(this.database, offset); break; } - database.setByte(0, type); + database.setByte(0, flags); + + if(storeSize) + { + byte[] bytes = DataHelpers.serializeInt(size); + + for(int i=0;i 65531) { // >= 2 ^ 16 + size_bytes = 4; + } else if(size > 253) { // >= 2 ^ 8 + size_bytes = 2; + } else { // < 2 ^ 8 + size_bytes = 1; + } - return database; + size += size_bytes; + last_seek = size; + + return size; } private String calcIndent(BdfIndent indent, int it) { @@ -116,36 +781,6 @@ public class BdfObject implements IBdfType return t; } - public String serializeHumanReadable(BdfIndent indent) { - return serializeHumanReadable(indent, 0); - } - - public String serializeHumanReadable() { - return serializeHumanReadable(new BdfIndent("", ""), 0); - } - - public String serializeHumanReadable(BdfIndent indent, int it) - { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - - try { - serializeHumanReadable(stream, indent, it); - return stream.toString(); - } - - catch(IOException e) { - return "undefined"; - } - } - - public void serializeHumanReadable(OutputStream stream, BdfIndent indent) throws IOException { - serializeHumanReadable(stream, indent, 0); - } - - public void serializeHumanReadable(OutputStream stream) throws IOException { - serializeHumanReadable(stream, new BdfIndent("", ""), 0); - } - public void serializeHumanReadable(OutputStream stream, BdfIndent indent, int it) throws IOException { String str = null; @@ -194,77 +829,77 @@ public class BdfObject implements IBdfType break; case BdfTypes.ARRAY_INTEGER: { - stream.write(("(" + calcIndent(indent, it)).getBytes()); + stream.write("int(".getBytes()); int[] array = this.getIntegerArray(); for(int i=0;i 2147483648L || number <= -2147483648L) { + setLong(number); + } else if(number > 32768 || number <= -32768) { + setInteger((int)number); + } else if(number > 128 || number <= -128) { + setShort((short)number); + } else { + setByte((byte)number); + } + + return this; } - public byte getType() { - return this.type; + public long getAutoInt() + { + switch(type) + { + case BdfTypes.BYTE: + return getByte(); + case BdfTypes.SHORT: + return getShort(); + case BdfTypes.INTEGER: + return getInteger(); + case BdfTypes.LONG: + return getLong(); + default: + return 0; + } } // Primitives @@ -492,7 +1151,7 @@ public class BdfObject implements IBdfType public BdfArray getArray() { if(this.type != BdfTypes.ARRAY) - this.setArray(); + setArray(newArray()); return (BdfArray)object; } @@ -500,7 +1159,7 @@ public class BdfObject implements IBdfType public BdfNamedList getNamedList() { if(this.type != BdfTypes.NAMED_LIST) - this.setNamedList(); + setNamedList(newNamedList()); return (BdfNamedList)object; } @@ -560,7 +1219,6 @@ public class BdfObject implements IBdfType public BdfObject setString(String value) { this.type = BdfTypes.STRING; - this.database = new BdfDatabase(value.getBytes()); this.object = value; return this; } @@ -577,12 +1235,28 @@ public class BdfObject implements IBdfType return this; } - public BdfObject setArray() { - return this.setArray(new BdfArray()); + public BdfObject newObject() { + return new BdfObject(lookupTable); } - public BdfObject setNamedList() { - return this.setNamedList(new BdfNamedList()); + public BdfNamedList newNamedList() { + return new BdfNamedList(lookupTable); + } + + public BdfArray newArray() { + return new BdfArray(lookupTable, 0); + } + + public BdfArray newArray(int size) { + return new BdfArray(lookupTable, size); + } + + public int getKeyLocation(String key) { + return lookupTable.getLocation(key.getBytes()); + } + + public String getKeyName(int key) { + return new String(lookupTable.getName(key)); } public BdfObject setBooleanArray(boolean[] value) { @@ -654,84 +1328,5 @@ public class BdfObject implements IBdfType database = DataHelpers.getDatabase(b); return this; } - - // Primitives - public static BdfObject withInteger(int v) { - return (new BdfObject()).setInteger(v); - } - - public static BdfObject withByte(byte v) { - return (new BdfObject()).setByte(v); - } - - public static BdfObject withBoolean(boolean v) { - return (new BdfObject()).setBoolean(v); - } - - public static BdfObject withFloat(float v) { - return (new BdfObject()).setFloat(v); - } - - public static BdfObject withDouble(double v) { - return (new BdfObject()).setDouble(v); - } - - public static BdfObject withLong(long v) { - return (new BdfObject()).setLong(v); - } - - public static BdfObject withShort(short v) { - return (new BdfObject()).setShort(v); - } - - // Arrays - public static BdfObject withIntegerArray(int[] v) { - return (new BdfObject()).setIntegerArray(v); - } - - public static BdfObject withByteArray(byte[] v) { - return (new BdfObject()).setByteArray(v); - } - - public static BdfObject withBooleanArray(boolean[] v) { - return (new BdfObject()).setBooleanArray(v); - } - - public static BdfObject withFloatArray(float[] v) { - return (new BdfObject()).setFloatArray(v); - } - - public static BdfObject withDoubleArray(double[] v) { - return (new BdfObject()).setDoubleArray(v); - } - - public static BdfObject withLongArray(long[] v) { - return (new BdfObject()).setLongArray(v); - } - - public static BdfObject withShortArray(short[] v) { - return (new BdfObject()).setShortArray(v); - } - - // Objects - public static BdfObject withString(String v) { - return (new BdfObject()).setString(v); - } - - public static BdfObject withArray(BdfArray v) { - return (new BdfObject()).setArray(v); - } - - public static BdfObject withNamedList(BdfNamedList v) { - return (new BdfObject()).setNamedList(v); - } - - public static BdfObject withArray() { - return (new BdfObject()).setArray(new BdfArray()); - } - - public static BdfObject withNamedList() { - return (new BdfObject()).setNamedList(new BdfNamedList()); - } } diff --git a/src/bdf/types/BdfReader.java b/src/bdf/types/BdfReader.java new file mode 100644 index 0000000..a61d220 --- /dev/null +++ b/src/bdf/types/BdfReader.java @@ -0,0 +1,182 @@ +package bdf.types; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +import bdf.data.BdfDatabase; +import bdf.data.IBdfDatabase; +import bdf.util.DataHelpers; + +public class BdfReader +{ + protected BdfLookupTable lookupTable; + protected BdfObject bdf; + + private void initNew() { + lookupTable = new BdfLookupTable(this); + bdf = new BdfObject(lookupTable); + } + + public BdfReader() { + initNew(); + } + + public BdfReader(byte[] database) { + this(new BdfDatabase(database)); + } + + public BdfReader(IBdfDatabase database) + { + try + { + if(database.size() == 0) { + initNew(); + return; + } + + int upto = 0; + + IBdfDatabase flag_ptr = database.getPointer(upto); + byte lookupTable_size_tag = BdfObject.getParentFlags(flag_ptr); + byte lookupTable_size_bytes = 0; + + switch(lookupTable_size_tag) + { + case 0: + lookupTable_size_bytes = 4; + break; + case 1: + lookupTable_size_bytes = 2; + break; + case 2: + lookupTable_size_bytes = 1; + break; + } + + if(lookupTable_size_bytes > database.size()) { + initNew(); + return; + } + + // Get the rest of the data + int bdf_size = BdfObject.getSize(flag_ptr); + + if(bdf_size + lookupTable_size_bytes > database.size()) { + initNew(); + return; + } + + IBdfDatabase database_bdf = database.getPointer(upto, bdf_size); + upto += bdf_size; + + // Get the lookup table + ByteBuffer lookupTable_size_buff = DataHelpers.getByteBuffer(database.getPointer(upto, lookupTable_size_bytes)); + int lookupTable_size = 0; + + switch(lookupTable_size_tag) + { + case 0: + lookupTable_size = lookupTable_size_buff.getInt(); + break; + case 1: + lookupTable_size = 0xffff & lookupTable_size_buff.getShort(); + break; + case 2: + lookupTable_size = 0xff & lookupTable_size_buff.get(); + break; + } + + if(lookupTable_size + lookupTable_size_bytes + bdf_size > database.size()) { + initNew(); + return; + } + + lookupTable = new BdfLookupTable(this, database.getPointer(lookupTable_size_bytes + upto, lookupTable_size)); + bdf = new BdfObject(lookupTable, database_bdf); + } + + catch(IndexOutOfBoundsException e) { + initNew(); + } + } + + public BdfDatabase serialize() + { + int[] locations = lookupTable.serializeGetLocations(); + + int bdf_size = bdf.serializeSeeker(locations); + int lookupTable_size = lookupTable.serializeSeeker(locations); + + int lookupTable_size_bytes = 0; + byte lookupTable_size_tag = 0; + + if(lookupTable_size > 65535) { // >= 2 ^ 16 + lookupTable_size_tag = 0; + lookupTable_size_bytes = 4; + } else if(lookupTable_size > 255) { // >= 2 ^ 8 + lookupTable_size_tag = 1; + lookupTable_size_bytes = 2; + } else { // < 2 ^ 8 + lookupTable_size_tag = 2; + lookupTable_size_bytes = 1; + } + + int upto = 0; + int database_size = bdf_size + lookupTable_size + lookupTable_size_bytes; + BdfDatabase database = new BdfDatabase(database_size); + + bdf.serialize(database.getPointer(upto, bdf_size), locations, lookupTable_size_tag); + upto += bdf_size; + + byte[] bytes = DataHelpers.serializeInt(lookupTable_size); + + for(int i=0;i 0x7e && c < 0xa1) || c == 0xad) + { + // Will be in the format \u0000 + size += 6; + } + + else + { + size += 1; + } + } + + char[] chars = new char[size]; + int upto = 0; + + for(int i=0;i 0x7e && c < 0xa1) || c == 0xad) + { + // Will be in the format \u0000 + chars[upto] = '\\'; + chars[upto+1] = 'u'; + chars[upto+2] = HEX[(c & 0xf000) >> 12]; + chars[upto+3] = HEX[(c & 0x0f00) >> 8]; + chars[upto+4] = HEX[(c & 0x00f0) >> 4]; + chars[upto+5] = HEX[(c & 0x000f)]; + upto += 6; + } + + else + { + chars[upto] = string_chars[i]; + upto += 1; + } + } + + string_chars = chars; + } // Add quotes to the string - serialized = "\"" + serialized + "\""; + string = "\"" + new String(string_chars) + "\""; // Return the serialized string - return serialized; + return string; } } diff --git a/src/bdf/util/FileHelpers.java b/src/bdf/util/FileHelpers.java deleted file mode 100644 index 7379320..0000000 --- a/src/bdf/util/FileHelpers.java +++ /dev/null @@ -1,86 +0,0 @@ -package bdf.util; - -import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.zip.InflaterInputStream; - -public class FileHelpers -{ - public static byte[] readAll(InputStream in) - { - try - { - // Get bytes to return - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - byte[] bytes = new byte[1024]; - int size = 0; - - while((size = in.read(bytes)) != -1) { - buffer.write(bytes, 0, size); - } - - // Send the bytes collected from the file stream back - return buffer.toByteArray(); - } - - catch (IOException e) - { - // Throw the IOException as a runtime exception - throw new RuntimeException(e); - } - } - - public static byte[] readAll(String path) throws IOException - { - // Create the file input stream - FileInputStream in = new FileInputStream(path); - - // Load all of its data - byte[] data = readAll(in); - - // Close the file input stream - in.close(); - - // Send back the data - return data; - } - - public static byte[] readAllCompressed(String path) throws IOException - { - // Create the file input stream - InflaterInputStream in = new InflaterInputStream(new FileInputStream(path)); - - // Load all of its data - byte[] data = readAll(in); - - // Close the file input stream - in.close(); - - // Send back the data - return data; - } - - public static byte[] readAllIgnoreErrors(String path) - { - try { - return readAll(path); - } - - catch(IOException e) { - return new byte[0]; - } - } - - public static byte[] readAllCompressedIgnoreErrors(String path) - { - try { - return readAllCompressed(path); - } - - catch(IOException | RuntimeException e) { - return new byte[0]; - } - } -} diff --git a/src/tests/Tests.java b/src/tests/Tests.java index ca7d29b..098ef5d 100755 --- a/src/tests/Tests.java +++ b/src/tests/Tests.java @@ -1,67 +1,56 @@ package tests; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; +import java.io.FileInputStream; import java.io.IOException; -import bdf.data.BdfDatabase; -import bdf.file.BdfFileManager; -import bdf.types.BdfArray; -import bdf.types.BdfIndent; -import bdf.types.BdfNamedList; -import bdf.types.BdfObject; +import bdf.data.IBdfDatabase; +import bdf.types.BdfReader; -public class Tests { - - public static void main(String[] args) throws InterruptedException, IOException +public class Tests +{ + public static void displayHex(IBdfDatabase db) { - /* - BdfObject bdf = new BdfObject(); - BdfNamedList nl = bdf.getNamedList(); + char[] hex_chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - byte[] bytes = new byte[1024*1024*1024]; - for(int i=0;i 1000) { + System.out.println("" + i + ": " + done); + start += 1000; + done = 0; + i += 1; + } + + done += 1; + + rand.read(buffer); + new BdfReader(buffer); } - - nl.get("next").setByteArray(bytes); - - BdfDatabase data = bdf.serialize(); - - FileOutputStream file = new FileOutputStream("./database.bdf"); - data.writeToStream(file); - */ - - - BdfObject bdf = new BdfObject(); - BdfArray a = bdf.getArray(); - - byte[] bytes = new byte[1024*1024*1024/2]; - for(int i=0;i