Merge branch 'release/1.0.0'
This commit is contained in:
		
							
								
								
									
										162
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | |||||||
|  | # Created by https://www.gitignore.io | ||||||
|  |  | ||||||
|  | *.log | ||||||
|  |  | ||||||
|  | ### Gradle ### | ||||||
|  | .gradle | ||||||
|  | build/ | ||||||
|  | classes/ | ||||||
|  |  | ||||||
|  | # Ignore Gradle GUI config | ||||||
|  | gradle-app.setting | ||||||
|  |  | ||||||
|  | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) | ||||||
|  | !gradle-wrapper.jar | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Intellij ### | ||||||
|  | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm | ||||||
|  |  | ||||||
|  | *.iml | ||||||
|  |  | ||||||
|  | ## Directory-based project format: | ||||||
|  | .idea/ | ||||||
|  | # if you remove the above rule, at least ignore the following: | ||||||
|  |  | ||||||
|  | # User-specific stuff: | ||||||
|  | # .idea/workspace.xml | ||||||
|  | # .idea/tasks.xml | ||||||
|  | # .idea/dictionaries | ||||||
|  |  | ||||||
|  | # Sensitive or high-churn files: | ||||||
|  | # .idea/dataSources.ids | ||||||
|  | # .idea/dataSources.xml | ||||||
|  | # .idea/sqlDataSources.xml | ||||||
|  | # .idea/dynamic.xml | ||||||
|  | # .idea/uiDesigner.xml | ||||||
|  |  | ||||||
|  | # Gradle: | ||||||
|  | # .idea/gradle.xml | ||||||
|  | # .idea/libraries | ||||||
|  |  | ||||||
|  | # Mongo Explorer plugin: | ||||||
|  | # .idea/mongoSettings.xml | ||||||
|  |  | ||||||
|  | ## File-based project format: | ||||||
|  | *.ipr | ||||||
|  | *.iws | ||||||
|  |  | ||||||
|  | ## Plugin-specific files: | ||||||
|  |  | ||||||
|  | # IntelliJ | ||||||
|  | /out/ | ||||||
|  |  | ||||||
|  | # mpeltonen/sbt-idea plugin | ||||||
|  | .idea_modules/ | ||||||
|  |  | ||||||
|  | # JIRA plugin | ||||||
|  | atlassian-ide-plugin.xml | ||||||
|  |  | ||||||
|  | # Crashlytics plugin (for Android Studio and IntelliJ) | ||||||
|  | com_crashlytics_export_strings.xml | ||||||
|  | crashlytics.properties | ||||||
|  | crashlytics-build.properties | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Eclipse ### | ||||||
|  | *.pydevproject | ||||||
|  | .metadata | ||||||
|  | .gradle | ||||||
|  | bin/ | ||||||
|  | tmp/ | ||||||
|  | *.tmp | ||||||
|  | *.bak | ||||||
|  | *.swp | ||||||
|  | *~.nib | ||||||
|  | local.properties | ||||||
|  | .settings/ | ||||||
|  | .loadpath | ||||||
|  |  | ||||||
|  | # Eclipse Core | ||||||
|  | .project | ||||||
|  |  | ||||||
|  | # External tool builders | ||||||
|  | .externalToolBuilders/ | ||||||
|  |  | ||||||
|  | # Locally stored "Eclipse launch configurations" | ||||||
|  | *.launch | ||||||
|  |  | ||||||
|  | # CDT-specific | ||||||
|  | .cproject | ||||||
|  |  | ||||||
|  | # JDT-specific (Eclipse Java Development Tools) | ||||||
|  | .classpath | ||||||
|  |  | ||||||
|  | # PDT-specific | ||||||
|  | .buildpath | ||||||
|  |  | ||||||
|  | # sbteclipse plugin | ||||||
|  | .target | ||||||
|  |  | ||||||
|  | # TeXlipse plugin | ||||||
|  | .texlipse | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Linux ### | ||||||
|  | *~ | ||||||
|  |  | ||||||
|  | # KDE directory preferences | ||||||
|  | .directory | ||||||
|  |  | ||||||
|  | # Linux trash folder which might appear on any partition or disk | ||||||
|  | .Trash-* | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### OSX ### | ||||||
|  | .DS_Store | ||||||
|  | .AppleDouble | ||||||
|  | .LSOverride | ||||||
|  |  | ||||||
|  | # Icon must end with two \r | ||||||
|  | Icon | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Thumbnails | ||||||
|  | ._* | ||||||
|  |  | ||||||
|  | # Files that might appear in the root of a volume | ||||||
|  | .DocumentRevisions-V100 | ||||||
|  | .fseventsd | ||||||
|  | .Spotlight-V100 | ||||||
|  | .TemporaryItems | ||||||
|  | .Trashes | ||||||
|  | .VolumeIcon.icns | ||||||
|  |  | ||||||
|  | # Directories potentially created on remote AFP share | ||||||
|  | .AppleDB | ||||||
|  | .AppleDesktop | ||||||
|  | Network Trash Folder | ||||||
|  | Temporary Items | ||||||
|  | .apdisk | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Windows ### | ||||||
|  | # Windows image file caches | ||||||
|  | Thumbs.db | ||||||
|  | ehthumbs.db | ||||||
|  |  | ||||||
|  | # Folder config file | ||||||
|  | Desktop.ini | ||||||
|  |  | ||||||
|  | # Recycle Bin used on file shares | ||||||
|  | $RECYCLE.BIN/ | ||||||
|  |  | ||||||
|  | # Windows Installer files | ||||||
|  | *.cab | ||||||
|  | *.msi | ||||||
|  | *.msm | ||||||
|  | *.msp | ||||||
|  |  | ||||||
|  | # Windows shortcuts | ||||||
|  | *.lnk | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | language: java | ||||||
|  | sudo: false # faster builds | ||||||
|  | jdk: | ||||||
|  |   - oraclejdk8 | ||||||
|  |  | ||||||
|  | before_install: | ||||||
|  |   - pip install --user codecov | ||||||
|  |  | ||||||
|  | after_success: | ||||||
|  |   - codecov | ||||||
							
								
								
									
										201
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | |||||||
|  |                                  Apache License | ||||||
|  |                            Version 2.0, January 2004 | ||||||
|  |                         http://www.apache.org/licenses/ | ||||||
|  |  | ||||||
|  |    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||||
|  |  | ||||||
|  |    1. Definitions. | ||||||
|  |  | ||||||
|  |       "License" shall mean the terms and conditions for use, reproduction, | ||||||
|  |       and distribution as defined by Sections 1 through 9 of this document. | ||||||
|  |  | ||||||
|  |       "Licensor" shall mean the copyright owner or entity authorized by | ||||||
|  |       the copyright owner that is granting the License. | ||||||
|  |  | ||||||
|  |       "Legal Entity" shall mean the union of the acting entity and all | ||||||
|  |       other entities that control, are controlled by, or are under common | ||||||
|  |       control with that entity. For the purposes of this definition, | ||||||
|  |       "control" means (i) the power, direct or indirect, to cause the | ||||||
|  |       direction or management of such entity, whether by contract or | ||||||
|  |       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||||
|  |       outstanding shares, or (iii) beneficial ownership of such entity. | ||||||
|  |  | ||||||
|  |       "You" (or "Your") shall mean an individual or Legal Entity | ||||||
|  |       exercising permissions granted by this License. | ||||||
|  |  | ||||||
|  |       "Source" form shall mean the preferred form for making modifications, | ||||||
|  |       including but not limited to software source code, documentation | ||||||
|  |       source, and configuration files. | ||||||
|  |  | ||||||
|  |       "Object" form shall mean any form resulting from mechanical | ||||||
|  |       transformation or translation of a Source form, including but | ||||||
|  |       not limited to compiled object code, generated documentation, | ||||||
|  |       and conversions to other media types. | ||||||
|  |  | ||||||
|  |       "Work" shall mean the work of authorship, whether in Source or | ||||||
|  |       Object form, made available under the License, as indicated by a | ||||||
|  |       copyright notice that is included in or attached to the work | ||||||
|  |       (an example is provided in the Appendix below). | ||||||
|  |  | ||||||
|  |       "Derivative Works" shall mean any work, whether in Source or Object | ||||||
|  |       form, that is based on (or derived from) the Work and for which the | ||||||
|  |       editorial revisions, annotations, elaborations, or other modifications | ||||||
|  |       represent, as a whole, an original work of authorship. For the purposes | ||||||
|  |       of this License, Derivative Works shall not include works that remain | ||||||
|  |       separable from, or merely link (or bind by name) to the interfaces of, | ||||||
|  |       the Work and Derivative Works thereof. | ||||||
|  |  | ||||||
|  |       "Contribution" shall mean any work of authorship, including | ||||||
|  |       the original version of the Work and any modifications or additions | ||||||
|  |       to that Work or Derivative Works thereof, that is intentionally | ||||||
|  |       submitted to Licensor for inclusion in the Work by the copyright owner | ||||||
|  |       or by an individual or Legal Entity authorized to submit on behalf of | ||||||
|  |       the copyright owner. For the purposes of this definition, "submitted" | ||||||
|  |       means any form of electronic, verbal, or written communication sent | ||||||
|  |       to the Licensor or its representatives, including but not limited to | ||||||
|  |       communication on electronic mailing lists, source code control systems, | ||||||
|  |       and issue tracking systems that are managed by, or on behalf of, the | ||||||
|  |       Licensor for the purpose of discussing and improving the Work, but | ||||||
|  |       excluding communication that is conspicuously marked or otherwise | ||||||
|  |       designated in writing by the copyright owner as "Not a Contribution." | ||||||
|  |  | ||||||
|  |       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||||
|  |       on behalf of whom a Contribution has been received by Licensor and | ||||||
|  |       subsequently incorporated within the Work. | ||||||
|  |  | ||||||
|  |    2. Grant of Copyright License. Subject to the terms and conditions of | ||||||
|  |       this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |       copyright license to reproduce, prepare Derivative Works of, | ||||||
|  |       publicly display, publicly perform, sublicense, and distribute the | ||||||
|  |       Work and such Derivative Works in Source or Object form. | ||||||
|  |  | ||||||
|  |    3. Grant of Patent License. Subject to the terms and conditions of | ||||||
|  |       this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |       (except as stated in this section) patent license to make, have made, | ||||||
|  |       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||||
|  |       where such license applies only to those patent claims licensable | ||||||
|  |       by such Contributor that are necessarily infringed by their | ||||||
|  |       Contribution(s) alone or by combination of their Contribution(s) | ||||||
|  |       with the Work to which such Contribution(s) was submitted. If You | ||||||
|  |       institute patent litigation against any entity (including a | ||||||
|  |       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||||
|  |       or a Contribution incorporated within the Work constitutes direct | ||||||
|  |       or contributory patent infringement, then any patent licenses | ||||||
|  |       granted to You under this License for that Work shall terminate | ||||||
|  |       as of the date such litigation is filed. | ||||||
|  |  | ||||||
|  |    4. Redistribution. You may reproduce and distribute copies of the | ||||||
|  |       Work or Derivative Works thereof in any medium, with or without | ||||||
|  |       modifications, and in Source or Object form, provided that You | ||||||
|  |       meet the following conditions: | ||||||
|  |  | ||||||
|  |       (a) You must give any other recipients of the Work or | ||||||
|  |           Derivative Works a copy of this License; and | ||||||
|  |  | ||||||
|  |       (b) You must cause any modified files to carry prominent notices | ||||||
|  |           stating that You changed the files; and | ||||||
|  |  | ||||||
|  |       (c) You must retain, in the Source form of any Derivative Works | ||||||
|  |           that You distribute, all copyright, patent, trademark, and | ||||||
|  |           attribution notices from the Source form of the Work, | ||||||
|  |           excluding those notices that do not pertain to any part of | ||||||
|  |           the Derivative Works; and | ||||||
|  |  | ||||||
|  |       (d) If the Work includes a "NOTICE" text file as part of its | ||||||
|  |           distribution, then any Derivative Works that You distribute must | ||||||
|  |           include a readable copy of the attribution notices contained | ||||||
|  |           within such NOTICE file, excluding those notices that do not | ||||||
|  |           pertain to any part of the Derivative Works, in at least one | ||||||
|  |           of the following places: within a NOTICE text file distributed | ||||||
|  |           as part of the Derivative Works; within the Source form or | ||||||
|  |           documentation, if provided along with the Derivative Works; or, | ||||||
|  |           within a display generated by the Derivative Works, if and | ||||||
|  |           wherever such third-party notices normally appear. The contents | ||||||
|  |           of the NOTICE file are for informational purposes only and | ||||||
|  |           do not modify the License. You may add Your own attribution | ||||||
|  |           notices within Derivative Works that You distribute, alongside | ||||||
|  |           or as an addendum to the NOTICE text from the Work, provided | ||||||
|  |           that such additional attribution notices cannot be construed | ||||||
|  |           as modifying the License. | ||||||
|  |  | ||||||
|  |       You may add Your own copyright statement to Your modifications and | ||||||
|  |       may provide additional or different license terms and conditions | ||||||
|  |       for use, reproduction, or distribution of Your modifications, or | ||||||
|  |       for any such Derivative Works as a whole, provided Your use, | ||||||
|  |       reproduction, and distribution of the Work otherwise complies with | ||||||
|  |       the conditions stated in this License. | ||||||
|  |  | ||||||
|  |    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||||
|  |       any Contribution intentionally submitted for inclusion in the Work | ||||||
|  |       by You to the Licensor shall be under the terms and conditions of | ||||||
|  |       this License, without any additional terms or conditions. | ||||||
|  |       Notwithstanding the above, nothing herein shall supersede or modify | ||||||
|  |       the terms of any separate license agreement you may have executed | ||||||
|  |       with Licensor regarding such Contributions. | ||||||
|  |  | ||||||
|  |    6. Trademarks. This License does not grant permission to use the trade | ||||||
|  |       names, trademarks, service marks, or product names of the Licensor, | ||||||
|  |       except as required for reasonable and customary use in describing the | ||||||
|  |       origin of the Work and reproducing the content of the NOTICE file. | ||||||
|  |  | ||||||
|  |    7. Disclaimer of Warranty. Unless required by applicable law or | ||||||
|  |       agreed to in writing, Licensor provides the Work (and each | ||||||
|  |       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||||
|  |       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||||
|  |       implied, including, without limitation, any warranties or conditions | ||||||
|  |       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||||
|  |       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||||
|  |       appropriateness of using or redistributing the Work and assume any | ||||||
|  |       risks associated with Your exercise of permissions under this License. | ||||||
|  |  | ||||||
|  |    8. Limitation of Liability. In no event and under no legal theory, | ||||||
|  |       whether in tort (including negligence), contract, or otherwise, | ||||||
|  |       unless required by applicable law (such as deliberate and grossly | ||||||
|  |       negligent acts) or agreed to in writing, shall any Contributor be | ||||||
|  |       liable to You for damages, including any direct, indirect, special, | ||||||
|  |       incidental, or consequential damages of any character arising as a | ||||||
|  |       result of this License or out of the use or inability to use the | ||||||
|  |       Work (including but not limited to damages for loss of goodwill, | ||||||
|  |       work stoppage, computer failure or malfunction, or any and all | ||||||
|  |       other commercial damages or losses), even if such Contributor | ||||||
|  |       has been advised of the possibility of such damages. | ||||||
|  |  | ||||||
|  |    9. Accepting Warranty or Additional Liability. While redistributing | ||||||
|  |       the Work or Derivative Works thereof, You may choose to offer, | ||||||
|  |       and charge a fee for, acceptance of support, warranty, indemnity, | ||||||
|  |       or other liability obligations and/or rights consistent with this | ||||||
|  |       License. However, in accepting such obligations, You may act only | ||||||
|  |       on Your own behalf and on Your sole responsibility, not on behalf | ||||||
|  |       of any other Contributor, and only if You agree to indemnify, | ||||||
|  |       defend, and hold each Contributor harmless for any liability | ||||||
|  |       incurred by, or claims asserted against, such Contributor by reason | ||||||
|  |       of your accepting any such warranty or additional liability. | ||||||
|  |  | ||||||
|  |    END OF TERMS AND CONDITIONS | ||||||
|  |  | ||||||
|  |    APPENDIX: How to apply the Apache License to your work. | ||||||
|  |  | ||||||
|  |       To apply the Apache License to your work, attach the following | ||||||
|  |       boilerplate notice, with the fields enclosed by brackets "{}" | ||||||
|  |       replaced with your own identifying information. (Don't include | ||||||
|  |       the brackets!)  The text should be enclosed in the appropriate | ||||||
|  |       comment syntax for the file format. We also recommend that a | ||||||
|  |       file or class name and description of purpose be included on the | ||||||
|  |       same "printed page" as the copyright notice for easier | ||||||
|  |       identification within third-party archives. | ||||||
|  |  | ||||||
|  |    Copyright {yyyy} {name of copyright owner} | ||||||
|  |  | ||||||
|  |    Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |    you may not use this file except in compliance with the License. | ||||||
|  |    You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |        http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  |    Unless required by applicable law or agreed to in writing, software | ||||||
|  |    distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |    See the License for the specific language governing permissions and | ||||||
|  |    limitations under the License. | ||||||
							
								
								
									
										87
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | Simple MessagePack | ||||||
|  | ================== | ||||||
|  | [](https://maven-badges.herokuapp.com/maven-central/ch.dissem.msgpack/msgpack) | ||||||
|  | [](http://www.javadoc.io/doc/ch.dissem.msgpack/msgpack) | ||||||
|  | [](https://raw.githubusercontent.com/Dissem/Jabit/master/LICENSE) | ||||||
|  |  | ||||||
|  | This is a simple Java library for handling MessagePack data. It doesn't do any object mapping, but maps to special | ||||||
|  | objects representing MessagePack types. To build, use command `./gradlew build`. | ||||||
|  |  | ||||||
|  | For most cases you might be better off using `org.msgpack:msgpack`, but I found that I needed something that generically | ||||||
|  | represents the internal structure of the data. | ||||||
|  |  | ||||||
|  | _Simple MessagePack_ uses Semantic Versioning, meaning as long as the major version doesn't change, nothing should break if you | ||||||
|  | update. Be aware though that this doesn't necessarily applies for SNAPSHOT builds and the development branch. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #### Master | ||||||
|  | [](https://travis-ci.org/Dissem/MsgPack)  | ||||||
|  | [](https://www.codacy.com/app/chrigu-meyer/MsgPack/dashboard?bid=4122049) | ||||||
|  | [](https://codecov.io/github/Dissem/MsgPack?branch=master) | ||||||
|  |  | ||||||
|  | #### Develop | ||||||
|  | [](https://travis-ci.org/Dissem/MsgPack?branch=develop)  | ||||||
|  | [](https://www.codacy.com/app/chrigu-meyer/MsgPack/dashboard?bid=4118049) | ||||||
|  | [](https://codecov.io/github/Dissem/MsgPack?branch=develop) | ||||||
|  |  | ||||||
|  | Limitations | ||||||
|  | -------------- | ||||||
|  |  | ||||||
|  | * There is no fallback to BigInteger for large integer type numbers, so there might be an integer overflow when reading | ||||||
|  |   too large numbers | ||||||
|  | * `MPFloat` uses the data type you're using to decide on precision (float 32 or 64) - not the actual value. E.g. 0.5 | ||||||
|  |   could be saved perfectly as a float 42, but if you provide a double value, it will be stored as float 64, wasting | ||||||
|  |   4 bytes. | ||||||
|  | * If you want to use the 'ext format family', you'll need to implement and register your own `MPType` and | ||||||
|  |   `MPType.Unpacker`. | ||||||
|  | * Be aware that custom `MPType.Unpacker` take precedence over the default unpackers, i.e. if you accidentally define | ||||||
|  |   your unpacker to handle strings, for example, you won't be able to unpack any regular strings anymore. | ||||||
|  |  | ||||||
|  | Setup | ||||||
|  | ----- | ||||||
|  |  | ||||||
|  | Add msgpack as Gradle dependency: | ||||||
|  | ```Gradle | ||||||
|  | compile "ch.dissem.msgpack:msgpack:1.0.0" | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Usage | ||||||
|  | ----- | ||||||
|  |  | ||||||
|  | ### Serialize Data | ||||||
|  |  | ||||||
|  | First, you'll need to create some msgpack objects to serialize: | ||||||
|  | ```Java | ||||||
|  | MPMap<MPString, MPType<?>> object = new MPMap<>(); | ||||||
|  | object.put(new MPString("compact"), new MPBoolean(true)); | ||||||
|  | object.put(new MPString("schema"), new MPInteger(0)); | ||||||
|  | ``` | ||||||
|  | or the shorthand version for simple types: | ||||||
|  | ```Java | ||||||
|  | import static ch.dissem.msgpack.types.Utils.mp; | ||||||
|  |  | ||||||
|  | MPMap<MPString, MPType<?>> object = new MPMap<>(); | ||||||
|  | object.put(mp("compact"), mp(true)); | ||||||
|  | object.put(mp("schema"), mp(0)); | ||||||
|  | ``` | ||||||
|  | then just use `pack(OutputStream)`: | ||||||
|  | ```Java | ||||||
|  | OutputStream out = ...; | ||||||
|  | object.pack(out); | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Deserialize Data | ||||||
|  |  | ||||||
|  | For deserializing data there is the reader object: | ||||||
|  | ```Java | ||||||
|  | Reader reader = Reader.getInstance() | ||||||
|  | ``` | ||||||
|  | just use `reader.read(InputStream)`. Unfortunately you'll need to make sure you got what you expected, the following | ||||||
|  | example might result in `ClassCastException` at weird places: | ||||||
|  | ```Java | ||||||
|  | InputStream in = ...; | ||||||
|  | MPType read = reader.read(in); | ||||||
|  | MPMap<MPString, MPString> map = (MPMap<MPString, MPString>) read; | ||||||
|  | String value = map.get(mp("key")).getValue(); | ||||||
|  | ``` | ||||||
							
								
								
									
										92
									
								
								build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								build.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | apply plugin: 'java' | ||||||
|  | apply plugin: 'maven' | ||||||
|  | apply plugin: 'signing' | ||||||
|  | apply plugin: 'jacoco' | ||||||
|  | apply plugin: 'gitflow-version' | ||||||
|  |  | ||||||
|  | sourceCompatibility = 1.7 | ||||||
|  | group = 'ch.dissem.msgpack' | ||||||
|  |  | ||||||
|  | repositories { | ||||||
|  |     mavenCentral() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | dependencies { | ||||||
|  |     testCompile group: 'junit', name: 'junit', version: '4.11' | ||||||
|  | } | ||||||
|  |  | ||||||
|  | test { | ||||||
|  |     testLogging { | ||||||
|  |         exceptionFormat = 'full' | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | task javadocJar(type: Jar) { | ||||||
|  |     classifier = 'javadoc' | ||||||
|  |     from javadoc | ||||||
|  | } | ||||||
|  |  | ||||||
|  | task sourcesJar(type: Jar) { | ||||||
|  |     classifier = 'sources' | ||||||
|  |     from sourceSets.main.allSource | ||||||
|  | } | ||||||
|  |  | ||||||
|  | artifacts { | ||||||
|  |     archives javadocJar, sourcesJar | ||||||
|  | } | ||||||
|  |  | ||||||
|  | signing { | ||||||
|  |     required { isRelease && project.getProperties().get("signing.keyId")?.length() > 0 } | ||||||
|  |     sign configurations.archives | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uploadArchives { | ||||||
|  |     repositories { | ||||||
|  |         mavenDeployer { | ||||||
|  |             beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } | ||||||
|  |  | ||||||
|  |             repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { | ||||||
|  |                 authentication(userName: ossrhUsername, password: ossrhPassword) | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { | ||||||
|  |                 authentication(userName: ossrhUsername, password: ossrhPassword) | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             pom.project { | ||||||
|  |                 name 'msgpack' | ||||||
|  |                 packaging 'jar' | ||||||
|  |                 url 'https://dissem.ch/msgpack' | ||||||
|  |  | ||||||
|  |                 scm { | ||||||
|  |                     connection 'scm:git:https://git.dissem.ch/chris/MessagePack.git' | ||||||
|  |                     developerConnection 'scm:git:git@git.dissem.ch:chris/MessagePack.git' | ||||||
|  |                     url 'https://git.dissem.ch/chris/MessagePack.git' | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 licenses { | ||||||
|  |                     license { | ||||||
|  |                         name 'The Apache License, Version 2.0' | ||||||
|  |                         url 'http://www.apache.org/licenses/LICENSE-2.0.txt' | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 developers { | ||||||
|  |                     developer { | ||||||
|  |                         name 'Christian Basler' | ||||||
|  |                         email 'chrigu.meyer@gmail.com' | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | jacocoTestReport { | ||||||
|  |     reports { | ||||||
|  |         xml.enabled = true | ||||||
|  |         html.enabled = true | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | check.dependsOn jacocoTestReport | ||||||
| @@ -0,0 +1,57 @@ | |||||||
|  | package ch.dissem.gradle | ||||||
|  |  | ||||||
|  | import org.gradle.api.Plugin | ||||||
|  | import org.gradle.api.Project | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Sets the version as follows: | ||||||
|  |  * <ul> | ||||||
|  |  *     <li>If the branch is 'master', the version is set to the latest tag (which is expected to be set by Git flow)</li> | ||||||
|  |  *     <li>Otherwise, the version is set to the branch name, with '-SNAPSHOT' appended</li> | ||||||
|  |  * </ul> | ||||||
|  |  */ | ||||||
|  | class GitFlowVersion implements Plugin<Project> { | ||||||
|  |     def getBranch(Project project) { | ||||||
|  |         def stdout = new ByteArrayOutputStream() | ||||||
|  |         project.exec { | ||||||
|  |             commandLine 'git', 'rev-parse', '--abbrev-ref', 'HEAD' | ||||||
|  |             standardOutput = stdout | ||||||
|  |         } | ||||||
|  |         return stdout.toString().trim() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     def getTag(Project project) { | ||||||
|  |         def stdout = new ByteArrayOutputStream() | ||||||
|  |         project.exec { | ||||||
|  |             commandLine 'git', 'describe', '--abbrev=0' | ||||||
|  |             standardOutput = stdout | ||||||
|  |         } | ||||||
|  |         return stdout.toString().trim() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     def isRelease(Project project) { | ||||||
|  |         return "master" == getBranch(project); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     def getVersion(Project project) { | ||||||
|  |         if (project.ext.isRelease) { | ||||||
|  |             return getTag(project) | ||||||
|  |         } else { | ||||||
|  |             def branch = getBranch(project) | ||||||
|  |             if ("develop" == branch) { | ||||||
|  |                 return "development-SNAPSHOT" | ||||||
|  |             } | ||||||
|  |             return branch.replaceAll("/", "-") + "-SNAPSHOT" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     void apply(Project project) { | ||||||
|  |         project.ext.isRelease = isRelease(project) | ||||||
|  |         project.version = getVersion(project) | ||||||
|  |  | ||||||
|  |         project.task('version') << { | ||||||
|  |             println "Version deduced from git: '${project.version}'" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | implementation-class=ch.dissem.gradle.GitFlowVersion | ||||||
							
								
								
									
										11
									
								
								gradle.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gradle.properties
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | # Don't change this file - override those properties in the | ||||||
|  | # gradle.properties file in your home directory instead | ||||||
|  |  | ||||||
|  | signing.keyId= | ||||||
|  | signing.password= | ||||||
|  | #signing.secretKeyRingFile= | ||||||
|  |  | ||||||
|  | ossrhUsername= | ||||||
|  | ossrhPassword= | ||||||
|  |  | ||||||
|  | systemTestsEnabled=false | ||||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										6
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | #Tue Jan 17 07:22:12 CET 2017 | ||||||
|  | distributionBase=GRADLE_USER_HOME | ||||||
|  | distributionPath=wrapper/dists | ||||||
|  | zipStoreBase=GRADLE_USER_HOME | ||||||
|  | zipStorePath=wrapper/dists | ||||||
|  | distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-all.zip | ||||||
							
								
								
									
										169
									
								
								gradlew
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										169
									
								
								gradlew
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,169 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  |  | ||||||
|  | ############################################################################## | ||||||
|  | ## | ||||||
|  | ##  Gradle start up script for UN*X | ||||||
|  | ## | ||||||
|  | ############################################################################## | ||||||
|  |  | ||||||
|  | # Attempt to set APP_HOME | ||||||
|  | # Resolve links: $0 may be a link | ||||||
|  | PRG="$0" | ||||||
|  | # Need this for relative symlinks. | ||||||
|  | while [ -h "$PRG" ] ; do | ||||||
|  |     ls=`ls -ld "$PRG"` | ||||||
|  |     link=`expr "$ls" : '.*-> \(.*\)$'` | ||||||
|  |     if expr "$link" : '/.*' > /dev/null; then | ||||||
|  |         PRG="$link" | ||||||
|  |     else | ||||||
|  |         PRG=`dirname "$PRG"`"/$link" | ||||||
|  |     fi | ||||||
|  | done | ||||||
|  | SAVED="`pwd`" | ||||||
|  | cd "`dirname \"$PRG\"`/" >/dev/null | ||||||
|  | APP_HOME="`pwd -P`" | ||||||
|  | cd "$SAVED" >/dev/null | ||||||
|  |  | ||||||
|  | APP_NAME="Gradle" | ||||||
|  | APP_BASE_NAME=`basename "$0"` | ||||||
|  |  | ||||||
|  | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||||
|  | DEFAULT_JVM_OPTS="" | ||||||
|  |  | ||||||
|  | # Use the maximum available, or set MAX_FD != -1 to use that value. | ||||||
|  | MAX_FD="maximum" | ||||||
|  |  | ||||||
|  | warn ( ) { | ||||||
|  |     echo "$*" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | die ( ) { | ||||||
|  |     echo | ||||||
|  |     echo "$*" | ||||||
|  |     echo | ||||||
|  |     exit 1 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | # OS specific support (must be 'true' or 'false'). | ||||||
|  | cygwin=false | ||||||
|  | msys=false | ||||||
|  | darwin=false | ||||||
|  | nonstop=false | ||||||
|  | case "`uname`" in | ||||||
|  |   CYGWIN* ) | ||||||
|  |     cygwin=true | ||||||
|  |     ;; | ||||||
|  |   Darwin* ) | ||||||
|  |     darwin=true | ||||||
|  |     ;; | ||||||
|  |   MINGW* ) | ||||||
|  |     msys=true | ||||||
|  |     ;; | ||||||
|  |   NONSTOP* ) | ||||||
|  |     nonstop=true | ||||||
|  |     ;; | ||||||
|  | esac | ||||||
|  |  | ||||||
|  | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar | ||||||
|  |  | ||||||
|  | # Determine the Java command to use to start the JVM. | ||||||
|  | if [ -n "$JAVA_HOME" ] ; then | ||||||
|  |     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then | ||||||
|  |         # IBM's JDK on AIX uses strange locations for the executables | ||||||
|  |         JAVACMD="$JAVA_HOME/jre/sh/java" | ||||||
|  |     else | ||||||
|  |         JAVACMD="$JAVA_HOME/bin/java" | ||||||
|  |     fi | ||||||
|  |     if [ ! -x "$JAVACMD" ] ; then | ||||||
|  |         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME | ||||||
|  |  | ||||||
|  | Please set the JAVA_HOME variable in your environment to match the | ||||||
|  | location of your Java installation." | ||||||
|  |     fi | ||||||
|  | else | ||||||
|  |     JAVACMD="java" | ||||||
|  |     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | ||||||
|  |  | ||||||
|  | Please set the JAVA_HOME variable in your environment to match the | ||||||
|  | location of your Java installation." | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # Increase the maximum file descriptors if we can. | ||||||
|  | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then | ||||||
|  |     MAX_FD_LIMIT=`ulimit -H -n` | ||||||
|  |     if [ $? -eq 0 ] ; then | ||||||
|  |         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then | ||||||
|  |             MAX_FD="$MAX_FD_LIMIT" | ||||||
|  |         fi | ||||||
|  |         ulimit -n $MAX_FD | ||||||
|  |         if [ $? -ne 0 ] ; then | ||||||
|  |             warn "Could not set maximum file descriptor limit: $MAX_FD" | ||||||
|  |         fi | ||||||
|  |     else | ||||||
|  |         warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" | ||||||
|  |     fi | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # For Darwin, add options to specify how the application appears in the dock | ||||||
|  | if $darwin; then | ||||||
|  |     GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # For Cygwin, switch paths to Windows format before running java | ||||||
|  | if $cygwin ; then | ||||||
|  |     APP_HOME=`cygpath --path --mixed "$APP_HOME"` | ||||||
|  |     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` | ||||||
|  |     JAVACMD=`cygpath --unix "$JAVACMD"` | ||||||
|  |  | ||||||
|  |     # We build the pattern for arguments to be converted via cygpath | ||||||
|  |     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` | ||||||
|  |     SEP="" | ||||||
|  |     for dir in $ROOTDIRSRAW ; do | ||||||
|  |         ROOTDIRS="$ROOTDIRS$SEP$dir" | ||||||
|  |         SEP="|" | ||||||
|  |     done | ||||||
|  |     OURCYGPATTERN="(^($ROOTDIRS))" | ||||||
|  |     # Add a user-defined pattern to the cygpath arguments | ||||||
|  |     if [ "$GRADLE_CYGPATTERN" != "" ] ; then | ||||||
|  |         OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" | ||||||
|  |     fi | ||||||
|  |     # Now convert the arguments - kludge to limit ourselves to /bin/sh | ||||||
|  |     i=0 | ||||||
|  |     for arg in "$@" ; do | ||||||
|  |         CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` | ||||||
|  |         CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option | ||||||
|  |  | ||||||
|  |         if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition | ||||||
|  |             eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` | ||||||
|  |         else | ||||||
|  |             eval `echo args$i`="\"$arg\"" | ||||||
|  |         fi | ||||||
|  |         i=$((i+1)) | ||||||
|  |     done | ||||||
|  |     case $i in | ||||||
|  |         (0) set -- ;; | ||||||
|  |         (1) set -- "$args0" ;; | ||||||
|  |         (2) set -- "$args0" "$args1" ;; | ||||||
|  |         (3) set -- "$args0" "$args1" "$args2" ;; | ||||||
|  |         (4) set -- "$args0" "$args1" "$args2" "$args3" ;; | ||||||
|  |         (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; | ||||||
|  |         (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; | ||||||
|  |         (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; | ||||||
|  |         (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; | ||||||
|  |         (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; | ||||||
|  |     esac | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # Split up the JVM_OPTS And GRADLE_OPTS values into an value, following the shell quoting and substitution rules | ||||||
|  | function splitJvmOpts() { | ||||||
|  |     JVM_OPTS=("$@") | ||||||
|  | } | ||||||
|  | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS | ||||||
|  | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" | ||||||
|  |  | ||||||
|  | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong | ||||||
|  | if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then | ||||||
|  |   cd "$(dirname "$0")" | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" | ||||||
							
								
								
									
										84
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | @if "%DEBUG%" == "" @echo off | ||||||
|  | @rem ########################################################################## | ||||||
|  | @rem | ||||||
|  | @rem  Gradle startup script for Windows | ||||||
|  | @rem | ||||||
|  | @rem ########################################################################## | ||||||
|  |  | ||||||
|  | @rem Set local scope for the variables with windows NT shell | ||||||
|  | if "%OS%"=="Windows_NT" setlocal | ||||||
|  |  | ||||||
|  | set DIRNAME=%~dp0 | ||||||
|  | if "%DIRNAME%" == "" set DIRNAME=. | ||||||
|  | set APP_BASE_NAME=%~n0 | ||||||
|  | set APP_HOME=%DIRNAME% | ||||||
|  |  | ||||||
|  | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||||
|  | set DEFAULT_JVM_OPTS= | ||||||
|  |  | ||||||
|  | @rem Find java.exe | ||||||
|  | if defined JAVA_HOME goto findJavaFromJavaHome | ||||||
|  |  | ||||||
|  | set JAVA_EXE=java.exe | ||||||
|  | %JAVA_EXE% -version >NUL 2>&1 | ||||||
|  | if "%ERRORLEVEL%" == "0" goto init | ||||||
|  |  | ||||||
|  | echo. | ||||||
|  | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | ||||||
|  | echo. | ||||||
|  | echo Please set the JAVA_HOME variable in your environment to match the | ||||||
|  | echo location of your Java installation. | ||||||
|  |  | ||||||
|  | goto fail | ||||||
|  |  | ||||||
|  | :findJavaFromJavaHome | ||||||
|  | set JAVA_HOME=%JAVA_HOME:"=% | ||||||
|  | set JAVA_EXE=%JAVA_HOME%/bin/java.exe | ||||||
|  |  | ||||||
|  | if exist "%JAVA_EXE%" goto init | ||||||
|  |  | ||||||
|  | echo. | ||||||
|  | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% | ||||||
|  | echo. | ||||||
|  | echo Please set the JAVA_HOME variable in your environment to match the | ||||||
|  | echo location of your Java installation. | ||||||
|  |  | ||||||
|  | goto fail | ||||||
|  |  | ||||||
|  | :init | ||||||
|  | @rem Get command-line arguments, handling Windows variants | ||||||
|  |  | ||||||
|  | if not "%OS%" == "Windows_NT" goto win9xME_args | ||||||
|  |  | ||||||
|  | :win9xME_args | ||||||
|  | @rem Slurp the command line arguments. | ||||||
|  | set CMD_LINE_ARGS= | ||||||
|  | set _SKIP=2 | ||||||
|  |  | ||||||
|  | :win9xME_args_slurp | ||||||
|  | if "x%~1" == "x" goto execute | ||||||
|  |  | ||||||
|  | set CMD_LINE_ARGS=%* | ||||||
|  |  | ||||||
|  | :execute | ||||||
|  | @rem Setup the command line | ||||||
|  |  | ||||||
|  | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar | ||||||
|  |  | ||||||
|  | @rem Execute Gradle | ||||||
|  | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% | ||||||
|  |  | ||||||
|  | :end | ||||||
|  | @rem End local scope for the variables with windows NT shell | ||||||
|  | if "%ERRORLEVEL%"=="0" goto mainEnd | ||||||
|  |  | ||||||
|  | :fail | ||||||
|  | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of | ||||||
|  | rem the _cmd.exe /c_ return code! | ||||||
|  | if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 | ||||||
|  | exit /b 1 | ||||||
|  |  | ||||||
|  | :mainEnd | ||||||
|  | if "%OS%"=="Windows_NT" endlocal | ||||||
|  |  | ||||||
|  | :omega | ||||||
							
								
								
									
										2
									
								
								settings.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								settings.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | rootProject.name = 'msgpack' | ||||||
|  |  | ||||||
							
								
								
									
										65
									
								
								src/main/java/ch/dissem/msgpack/Reader.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/main/java/ch/dissem/msgpack/Reader.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack; | ||||||
|  |  | ||||||
|  | import ch.dissem.msgpack.types.*; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.util.LinkedList; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Reads MPType object from an {@link InputStream}. | ||||||
|  |  */ | ||||||
|  | public class Reader { | ||||||
|  |     private LinkedList<MPType.Unpacker<?>> unpackers = new LinkedList<>(); | ||||||
|  |  | ||||||
|  |     private static final Reader instance = new Reader(); | ||||||
|  |  | ||||||
|  |     private Reader() { | ||||||
|  |         unpackers.add(new MPNil.Unpacker()); | ||||||
|  |         unpackers.add(new MPBoolean.Unpacker()); | ||||||
|  |         unpackers.add(new MPInteger.Unpacker()); | ||||||
|  |         unpackers.add(new MPFloat.Unpacker()); | ||||||
|  |         unpackers.add(new MPString.Unpacker()); | ||||||
|  |         unpackers.add(new MPBinary.Unpacker()); | ||||||
|  |         unpackers.add(new MPMap.Unpacker(this)); | ||||||
|  |         unpackers.add(new MPArray.Unpacker(this)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static Reader getInstance() { | ||||||
|  |         return instance; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Register your own extensions | ||||||
|  |      */ | ||||||
|  |     public void register(MPType.Unpacker<?> unpacker) { | ||||||
|  |         unpackers.addFirst(unpacker); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public MPType read(InputStream in) throws IOException { | ||||||
|  |         int firstByte = in.read(); | ||||||
|  |         for (MPType.Unpacker<?> unpacker : unpackers) { | ||||||
|  |             if (unpacker.is(firstByte)) { | ||||||
|  |                 return unpacker.unpack(firstByte, in); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         throw new IOException(String.format("Unsupported input, no reader for 0x%02x", firstByte)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										256
									
								
								src/main/java/ch/dissem/msgpack/types/MPArray.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								src/main/java/ch/dissem/msgpack/types/MPArray.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,256 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack.types; | ||||||
|  |  | ||||||
|  | import ch.dissem.msgpack.Reader; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.nio.ByteBuffer; | ||||||
|  | import java.util.*; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Representation of a msgpack encoded array. Uses a list to represent data internally, and implements the {@link List} | ||||||
|  |  * interface for your convenience. | ||||||
|  |  * | ||||||
|  |  * @param <T> | ||||||
|  |  */ | ||||||
|  | public class MPArray<T extends MPType> implements MPType<List<T>>, List<T> { | ||||||
|  |     private List<T> array; | ||||||
|  |  | ||||||
|  |     public MPArray() { | ||||||
|  |         this.array = new LinkedList<>(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public MPArray(List<T> array) { | ||||||
|  |         this.array = array; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @SafeVarargs | ||||||
|  |     public MPArray(T... objects) { | ||||||
|  |         this.array = Arrays.asList(objects); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public List<T> getValue() { | ||||||
|  |         return array; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void pack(OutputStream out) throws IOException { | ||||||
|  |         int size = array.size(); | ||||||
|  |         if (size < 16) { | ||||||
|  |             out.write(0b10010000 + size); | ||||||
|  |         } else if (size < 65536) { | ||||||
|  |             out.write(0xDC); | ||||||
|  |             out.write(ByteBuffer.allocate(2).putShort((short) size).array()); | ||||||
|  |         } else { | ||||||
|  |             out.write(0xDD); | ||||||
|  |             out.write(ByteBuffer.allocate(4).putInt(size).array()); | ||||||
|  |         } | ||||||
|  |         for (MPType o : array) { | ||||||
|  |             o.pack(out); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int size() { | ||||||
|  |         return array.size(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean isEmpty() { | ||||||
|  |         return array.isEmpty(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean contains(Object o) { | ||||||
|  |         return array.contains(o); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Iterator<T> iterator() { | ||||||
|  |         return array.iterator(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Object[] toArray() { | ||||||
|  |         return array.toArray(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     @SuppressWarnings("SuspiciousToArrayCall") | ||||||
|  |     public <T1> T1[] toArray(T1[] t1s) { | ||||||
|  |         return array.toArray(t1s); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean add(T t) { | ||||||
|  |         return array.add(t); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean remove(Object o) { | ||||||
|  |         return array.remove(o); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean containsAll(Collection<?> collection) { | ||||||
|  |         return array.containsAll(collection); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean addAll(Collection<? extends T> collection) { | ||||||
|  |         return array.addAll(collection); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean addAll(int i, Collection<? extends T> collection) { | ||||||
|  |         return array.addAll(i, collection); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean removeAll(Collection<?> collection) { | ||||||
|  |         return array.removeAll(collection); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean retainAll(Collection<?> collection) { | ||||||
|  |         return array.retainAll(collection); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void clear() { | ||||||
|  |         array.clear(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean equals(Object o) { | ||||||
|  |         if (this == o) return true; | ||||||
|  |         if (o == null || getClass() != o.getClass()) return false; | ||||||
|  |         MPArray<?> mpArray = (MPArray<?>) o; | ||||||
|  |         return Objects.equals(array, mpArray.array); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         return Objects.hash(array); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public T get(int i) { | ||||||
|  |         return array.get(i); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public T set(int i, T t) { | ||||||
|  |         return array.set(i, t); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void add(int i, T t) { | ||||||
|  |         array.add(i, t); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public T remove(int i) { | ||||||
|  |         return array.remove(i); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int indexOf(Object o) { | ||||||
|  |         return array.indexOf(o); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int lastIndexOf(Object o) { | ||||||
|  |         return array.lastIndexOf(o); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ListIterator<T> listIterator() { | ||||||
|  |         return array.listIterator(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ListIterator<T> listIterator(int i) { | ||||||
|  |         return array.listIterator(i); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<T> subList(int fromIndex, int toIndex) { | ||||||
|  |         return array.subList(fromIndex, toIndex); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return toJson(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toJson() { | ||||||
|  |         return toJson(""); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     String toJson(String indent) { | ||||||
|  |         StringBuilder result = new StringBuilder(); | ||||||
|  |         result.append("[\n"); | ||||||
|  |         Iterator<T> iterator = array.iterator(); | ||||||
|  |         String indent2 = indent + "  "; | ||||||
|  |         while (iterator.hasNext()) { | ||||||
|  |             T item = iterator.next(); | ||||||
|  |             result.append(indent2); | ||||||
|  |             result.append(Utils.toJson(item, indent2)); | ||||||
|  |             if (iterator.hasNext()) { | ||||||
|  |                 result.append(','); | ||||||
|  |             } | ||||||
|  |             result.append('\n'); | ||||||
|  |         } | ||||||
|  |         result.append("]"); | ||||||
|  |         return result.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Unpacker implements MPType.Unpacker<MPArray> { | ||||||
|  |         private final Reader reader; | ||||||
|  |  | ||||||
|  |         public Unpacker(Reader reader) { | ||||||
|  |             this.reader = reader; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public boolean is(int firstByte) { | ||||||
|  |             return firstByte == 0xDC || firstByte == 0xDD || (firstByte & 0b11110000) == 0b10010000; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public MPArray<MPType<?>> unpack(int firstByte, InputStream in) throws IOException { | ||||||
|  |             int size; | ||||||
|  |             if ((firstByte & 0b11110000) == 0b10010000) { | ||||||
|  |                 size = firstByte & 0b00001111; | ||||||
|  |             } else if (firstByte == 0xDC) { | ||||||
|  |                 size = in.read() << 8 | in.read(); | ||||||
|  |             } else if (firstByte == 0xDD) { | ||||||
|  |                 size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read(); | ||||||
|  |             } else { | ||||||
|  |                 throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte)); | ||||||
|  |             } | ||||||
|  |             List<MPType<?>> list = new LinkedList<>(); | ||||||
|  |             for (int i = 0; i < size; i++) { | ||||||
|  |                 MPType value = reader.read(in); | ||||||
|  |                 list.add(value); | ||||||
|  |             } | ||||||
|  |             return new MPArray<>(list); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										98
									
								
								src/main/java/ch/dissem/msgpack/types/MPBinary.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/main/java/ch/dissem/msgpack/types/MPBinary.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack.types; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.nio.ByteBuffer; | ||||||
|  | import java.util.Arrays; | ||||||
|  |  | ||||||
|  | import static ch.dissem.msgpack.types.Utils.bytes; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Representation of msgpack encoded binary data a.k.a. byte array. | ||||||
|  |  */ | ||||||
|  | public class MPBinary implements MPType<byte[]> { | ||||||
|  |     private byte[] value; | ||||||
|  |  | ||||||
|  |     public MPBinary(byte[] value) { | ||||||
|  |         this.value = value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public byte[] getValue() { | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void pack(OutputStream out) throws IOException { | ||||||
|  |         int size = value.length; | ||||||
|  |         if (size < 256) { | ||||||
|  |             out.write(0xC4); | ||||||
|  |             out.write((byte) size); | ||||||
|  |         } else if (size < 65536) { | ||||||
|  |             out.write(0xC5); | ||||||
|  |             out.write(ByteBuffer.allocate(2).putShort((short) size).array()); | ||||||
|  |         } else { | ||||||
|  |             out.write(0xC6); | ||||||
|  |             out.write(ByteBuffer.allocate(4).putInt(size).array()); | ||||||
|  |         } | ||||||
|  |         out.write(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean equals(Object o) { | ||||||
|  |         if (this == o) return true; | ||||||
|  |         if (o == null || getClass() != o.getClass()) return false; | ||||||
|  |         MPBinary mpBinary = (MPBinary) o; | ||||||
|  |         return Arrays.equals(value, mpBinary.value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         return Arrays.hashCode(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return toJson(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toJson() { | ||||||
|  |         return Utils.base64(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Unpacker implements MPType.Unpacker<MPBinary> { | ||||||
|  |         public boolean is(int firstByte) { | ||||||
|  |             return firstByte == 0xC4 || firstByte == 0xC5 || firstByte == 0xC6; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public MPBinary unpack(int firstByte, InputStream in) throws IOException { | ||||||
|  |             int size; | ||||||
|  |             if (firstByte == 0xC4) { | ||||||
|  |                 size = in.read(); | ||||||
|  |             } else if (firstByte == 0xC5) { | ||||||
|  |                 size = in.read() << 8 | in.read(); | ||||||
|  |             } else if (firstByte == 0xC6) { | ||||||
|  |                 size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read(); | ||||||
|  |             } else { | ||||||
|  |                 throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte)); | ||||||
|  |             } | ||||||
|  |             return new MPBinary(bytes(in, size).array()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										88
									
								
								src/main/java/ch/dissem/msgpack/types/MPBoolean.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/main/java/ch/dissem/msgpack/types/MPBoolean.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack.types; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Representation of a msgpack encoded boolean. | ||||||
|  |  */ | ||||||
|  | public class MPBoolean implements MPType<Boolean> { | ||||||
|  |     private final static int FALSE = 0xC2; | ||||||
|  |     private final static int TRUE = 0xC3; | ||||||
|  |  | ||||||
|  |     private final boolean value; | ||||||
|  |  | ||||||
|  |     public MPBoolean(boolean value) { | ||||||
|  |         this.value = value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Boolean getValue() { | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void pack(OutputStream out) throws IOException { | ||||||
|  |         if (value) { | ||||||
|  |             out.write(TRUE); | ||||||
|  |         } else { | ||||||
|  |             out.write(FALSE); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean equals(Object o) { | ||||||
|  |         if (this == o) return true; | ||||||
|  |         if (o == null || getClass() != o.getClass()) return false; | ||||||
|  |         MPBoolean mpBoolean = (MPBoolean) o; | ||||||
|  |         return value == mpBoolean.value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         return Objects.hash(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return String.valueOf(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toJson() { | ||||||
|  |         return String.valueOf(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Unpacker implements MPType.Unpacker<MPBoolean> { | ||||||
|  |  | ||||||
|  |         public boolean is(int firstByte) { | ||||||
|  |             return firstByte == TRUE || firstByte == FALSE; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public MPBoolean unpack(int firstByte, InputStream in) { | ||||||
|  |             if (firstByte == TRUE) { | ||||||
|  |                 return new MPBoolean(true); | ||||||
|  |             } else if (firstByte == FALSE) { | ||||||
|  |                 return new MPBoolean(false); | ||||||
|  |             } else { | ||||||
|  |                 throw new IllegalArgumentException(String.format("0xC2 or 0xC3 expected but was 0x%02x", firstByte)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										110
									
								
								src/main/java/ch/dissem/msgpack/types/MPFloat.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/main/java/ch/dissem/msgpack/types/MPFloat.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack.types; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.nio.ByteBuffer; | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
|  | import static ch.dissem.msgpack.types.Utils.bytes; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Representation of a msgpack encoded float32 or float64 number. | ||||||
|  |  */ | ||||||
|  | public class MPFloat implements MPType<Double> { | ||||||
|  |  | ||||||
|  |     public enum Precision {FLOAT32, FLOAT64} | ||||||
|  |  | ||||||
|  |     private final double value; | ||||||
|  |     private final Precision precision; | ||||||
|  |  | ||||||
|  |     public MPFloat(float value) { | ||||||
|  |         this.value = value; | ||||||
|  |         this.precision = Precision.FLOAT32; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public MPFloat(double value) { | ||||||
|  |         this.value = value; | ||||||
|  |         this.precision = Precision.FLOAT64; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Double getValue() { | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Precision getPrecision() { | ||||||
|  |         return precision; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void pack(OutputStream out) throws IOException { | ||||||
|  |         switch (precision) { | ||||||
|  |             case FLOAT32: | ||||||
|  |                 out.write(0xCA); | ||||||
|  |                 out.write(ByteBuffer.allocate(4).putFloat((float) value).array()); | ||||||
|  |                 break; | ||||||
|  |             case FLOAT64: | ||||||
|  |                 out.write(0xCB); | ||||||
|  |                 out.write(ByteBuffer.allocate(8).putDouble(value).array()); | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 throw new IllegalArgumentException("Unknown precision: " + precision); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean equals(Object o) { | ||||||
|  |         if (this == o) return true; | ||||||
|  |         if (o == null || getClass() != o.getClass()) return false; | ||||||
|  |         MPFloat mpFloat = (MPFloat) o; | ||||||
|  |         return Double.compare(mpFloat.value, value) == 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         return Objects.hash(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return String.valueOf(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toJson() { | ||||||
|  |         return String.valueOf(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Unpacker implements MPType.Unpacker<MPFloat> { | ||||||
|  |         public boolean is(int firstByte) { | ||||||
|  |             return firstByte == 0xCA || firstByte == 0xCB; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public MPFloat unpack(int firstByte, InputStream in) throws IOException { | ||||||
|  |             switch (firstByte) { | ||||||
|  |                 case 0xCA: | ||||||
|  |                     return new MPFloat(bytes(in, 4).getFloat()); | ||||||
|  |                 case 0xCB: | ||||||
|  |                     return new MPFloat(bytes(in, 8).getDouble()); | ||||||
|  |                 default: | ||||||
|  |                     throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										160
									
								
								src/main/java/ch/dissem/msgpack/types/MPInteger.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								src/main/java/ch/dissem/msgpack/types/MPInteger.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack.types; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.nio.ByteBuffer; | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
|  | import static ch.dissem.msgpack.types.Utils.bytes; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Representation of a msgpack encoded integer. The encoding is automatically selected according to the value's size. | ||||||
|  |  * Uses long due to the fact that the msgpack integer implementation may contain up to 64 bit numbers, corresponding | ||||||
|  |  * to Java long values. Also note that uint64 values may be too large for signed long (thanks Java for not supporting | ||||||
|  |  * unsigned values) and end in a negative value. | ||||||
|  |  */ | ||||||
|  | public class MPInteger implements MPType<Long> { | ||||||
|  |     private long value; | ||||||
|  |  | ||||||
|  |     public MPInteger(long value) { | ||||||
|  |         this.value = value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Long getValue() { | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void pack(OutputStream out) throws IOException { | ||||||
|  |         if ((value > ((byte) 0b11100000) && value < 0x80)) { | ||||||
|  |             out.write((int) value); | ||||||
|  |         } else if (value > 0) { | ||||||
|  |             if (value <= 0xFF) { | ||||||
|  |                 out.write(0xCC); | ||||||
|  |                 out.write((int) value); | ||||||
|  |             } else if (value <= 0xFFFF) { | ||||||
|  |                 out.write(0xCD); | ||||||
|  |                 out.write(ByteBuffer.allocate(2).putShort((short) value).array()); | ||||||
|  |             } else if (value <= 0xFFFFFFFFL) { | ||||||
|  |                 out.write(0xCE); | ||||||
|  |                 out.write(ByteBuffer.allocate(4).putInt((int) value).array()); | ||||||
|  |             } else { | ||||||
|  |                 out.write(0xCF); | ||||||
|  |                 out.write(ByteBuffer.allocate(8).putLong(value).array()); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             if (value >= -32) { | ||||||
|  |                 out.write(new byte[]{ | ||||||
|  |                         (byte) value | ||||||
|  |                 }); | ||||||
|  |             } else if (value >= Byte.MIN_VALUE) { | ||||||
|  |                 out.write(0xD0); | ||||||
|  |                 out.write(ByteBuffer.allocate(1).put((byte) value).array()); | ||||||
|  |             } else if (value >= Short.MIN_VALUE) { | ||||||
|  |                 out.write(0xD1); | ||||||
|  |                 out.write(ByteBuffer.allocate(2).putShort((short) value).array()); | ||||||
|  |             } else if (value >= Integer.MIN_VALUE) { | ||||||
|  |                 out.write(0xD2); | ||||||
|  |                 out.write(ByteBuffer.allocate(4).putInt((int) value).array()); | ||||||
|  |             } else { | ||||||
|  |                 out.write(0xD3); | ||||||
|  |                 out.write(ByteBuffer.allocate(8).putLong(value).array()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean equals(Object o) { | ||||||
|  |         if (this == o) return true; | ||||||
|  |         if (o == null || getClass() != o.getClass()) return false; | ||||||
|  |         MPInteger mpInteger = (MPInteger) o; | ||||||
|  |         return value == mpInteger.value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         return Objects.hash(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return String.valueOf(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toJson() { | ||||||
|  |         return String.valueOf(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Unpacker implements MPType.Unpacker<MPInteger> { | ||||||
|  |         public boolean is(int firstByte) { | ||||||
|  |             switch (firstByte) { | ||||||
|  |                 case 0xCC: | ||||||
|  |                 case 0xCD: | ||||||
|  |                 case 0xCE: | ||||||
|  |                 case 0xCF: | ||||||
|  |                 case 0xD0: | ||||||
|  |                 case 0xD1: | ||||||
|  |                 case 0xD2: | ||||||
|  |                 case 0xD3: | ||||||
|  |                     return true; | ||||||
|  |                 default: | ||||||
|  |                     return (firstByte & 0b10000000) == 0 || (firstByte & 0b11100000) == 0b11100000; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public MPInteger unpack(int firstByte, InputStream in) throws IOException { | ||||||
|  |             if ((firstByte & 0b10000000) == 0 || (firstByte & 0b11100000) == 0b11100000) { | ||||||
|  |                 return new MPInteger((byte) firstByte); | ||||||
|  |             } else { | ||||||
|  |                 switch (firstByte) { | ||||||
|  |                     case 0xCC: | ||||||
|  |                         return new MPInteger(in.read()); | ||||||
|  |                     case 0xCD: | ||||||
|  |                         return new MPInteger(in.read() << 8 | in.read()); | ||||||
|  |                     case 0xCE: { | ||||||
|  |                         long value = 0; | ||||||
|  |                         for (int i = 0; i < 4; i++) { | ||||||
|  |                             value = value << 8 | in.read(); | ||||||
|  |                         } | ||||||
|  |                         return new MPInteger(value); | ||||||
|  |                     } | ||||||
|  |                     case 0xCF: { | ||||||
|  |                         long value = 0; | ||||||
|  |                         for (int i = 0; i < 8; i++) { | ||||||
|  |                             value = value << 8 | in.read(); | ||||||
|  |                         } | ||||||
|  |                         return new MPInteger(value); | ||||||
|  |                     } | ||||||
|  |                     case 0xD0: | ||||||
|  |                         return new MPInteger(bytes(in, 1).get()); | ||||||
|  |                     case 0xD1: | ||||||
|  |                         return new MPInteger(bytes(in, 2).getShort()); | ||||||
|  |                     case 0xD2: | ||||||
|  |                         return new MPInteger(bytes(in, 4).getInt()); | ||||||
|  |                     case 0xD3: | ||||||
|  |                         return new MPInteger(bytes(in, 8).getLong()); | ||||||
|  |                     default: | ||||||
|  |                         throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte)); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										185
									
								
								src/main/java/ch/dissem/msgpack/types/MPMap.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								src/main/java/ch/dissem/msgpack/types/MPMap.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,185 @@ | |||||||
|  | package ch.dissem.msgpack.types; | ||||||
|  |  | ||||||
|  | import ch.dissem.msgpack.Reader; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.nio.ByteBuffer; | ||||||
|  | import java.util.*; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Representation of a msgpack encoded map. It is recommended to use a {@link LinkedHashMap} to ensure the order | ||||||
|  |  * of entries. For convenience, it also implements the {@link Map} interface. | ||||||
|  |  * | ||||||
|  |  * @param <K> | ||||||
|  |  * @param <V> | ||||||
|  |  */ | ||||||
|  | public class MPMap<K extends MPType, V extends MPType> implements MPType<Map<K, V>>, Map<K, V> { | ||||||
|  |     private Map<K, V> map; | ||||||
|  |  | ||||||
|  |     public MPMap() { | ||||||
|  |         this.map = new LinkedHashMap<>(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public MPMap(Map<K, V> map) { | ||||||
|  |         this.map = map; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Map<K, V> getValue() { | ||||||
|  |         return map; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void pack(OutputStream out) throws IOException { | ||||||
|  |         int size = map.size(); | ||||||
|  |         if (size < 16) { | ||||||
|  |             out.write(0x80 + size); | ||||||
|  |         } else if (size < 65536) { | ||||||
|  |             out.write(0xDE); | ||||||
|  |             out.write(ByteBuffer.allocate(2).putShort((short) size).array()); | ||||||
|  |         } else { | ||||||
|  |             out.write(0xDF); | ||||||
|  |             out.write(ByteBuffer.allocate(4).putInt(size).array()); | ||||||
|  |         } | ||||||
|  |         for (Map.Entry<K, V> e : map.entrySet()) { | ||||||
|  |             e.getKey().pack(out); | ||||||
|  |             e.getValue().pack(out); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int size() { | ||||||
|  |         return map.size(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean isEmpty() { | ||||||
|  |         return map.isEmpty(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean containsKey(Object o) { | ||||||
|  |         return map.containsKey(o); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean containsValue(Object o) { | ||||||
|  |         return map.containsValue(o); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public V get(Object o) { | ||||||
|  |         return map.get(o); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public V put(K k, V v) { | ||||||
|  |         return map.put(k, v); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public V remove(Object o) { | ||||||
|  |         return map.remove(o); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void putAll(Map<? extends K, ? extends V> map) { | ||||||
|  |         this.map.putAll(map); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void clear() { | ||||||
|  |         map.clear(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Set<K> keySet() { | ||||||
|  |         return map.keySet(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Collection<V> values() { | ||||||
|  |         return map.values(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Set<Entry<K, V>> entrySet() { | ||||||
|  |         return map.entrySet(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean equals(Object o) { | ||||||
|  |         if (this == o) return true; | ||||||
|  |         if (o == null || getClass() != o.getClass()) return false; | ||||||
|  |         MPMap<?, ?> mpMap = (MPMap<?, ?>) o; | ||||||
|  |         return Objects.equals(map, mpMap.map); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         return Objects.hash(map); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return toJson(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     String toJson(String indent) { | ||||||
|  |         StringBuilder result = new StringBuilder(); | ||||||
|  |         result.append("{\n"); | ||||||
|  |         Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator(); | ||||||
|  |         String indent2 = indent + "  "; | ||||||
|  |         while (iterator.hasNext()) { | ||||||
|  |             Map.Entry<K, V> item = iterator.next(); | ||||||
|  |             result.append(indent2); | ||||||
|  |             result.append(Utils.toJson(item.getKey(), indent2)); | ||||||
|  |             result.append(": "); | ||||||
|  |             result.append(Utils.toJson(item.getValue(), indent2)); | ||||||
|  |             if (iterator.hasNext()) { | ||||||
|  |                 result.append(','); | ||||||
|  |             } | ||||||
|  |             result.append('\n'); | ||||||
|  |         } | ||||||
|  |         result.append(indent).append("}"); | ||||||
|  |         return result.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toJson() { | ||||||
|  |         return toJson(""); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Unpacker implements MPType.Unpacker<MPMap> { | ||||||
|  |         private final Reader reader; | ||||||
|  |  | ||||||
|  |         public Unpacker(Reader reader) { | ||||||
|  |             this.reader = reader; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public boolean is(int firstByte) { | ||||||
|  |             return firstByte == 0xDE || firstByte == 0xDF || (firstByte & 0xF0) == 0x80; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public MPMap<MPType<?>, MPType<?>> unpack(int firstByte, InputStream in) throws IOException { | ||||||
|  |             int size; | ||||||
|  |             if ((firstByte & 0xF0) == 0x80) { | ||||||
|  |                 size = firstByte & 0x0F; | ||||||
|  |             } else if (firstByte == 0xDE) { | ||||||
|  |                 size = in.read() << 8 | in.read(); | ||||||
|  |             } else if (firstByte == 0xDF) { | ||||||
|  |                 size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read(); | ||||||
|  |             } else { | ||||||
|  |                 throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte)); | ||||||
|  |             } | ||||||
|  |             Map<MPType<?>, MPType<?>> map = new LinkedHashMap<>(); | ||||||
|  |             for (int i = 0; i < size; i++) { | ||||||
|  |                 MPType key = reader.read(in); | ||||||
|  |                 MPType value = reader.read(in); | ||||||
|  |                 map.put(key, value); | ||||||
|  |             } | ||||||
|  |             return new MPMap<>(map); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										70
									
								
								src/main/java/ch/dissem/msgpack/types/MPNil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/main/java/ch/dissem/msgpack/types/MPNil.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack.types; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Representation of msgpack encoded nil / null. | ||||||
|  |  */ | ||||||
|  | public class MPNil implements MPType<Void> { | ||||||
|  |     private final static int NIL = 0xC0; | ||||||
|  |  | ||||||
|  |     public Void getValue() { | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void pack(OutputStream out) throws IOException { | ||||||
|  |         out.write(NIL); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean equals(Object o) { | ||||||
|  |         return o instanceof MPNil; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return "null"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toJson() { | ||||||
|  |         return "null"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Unpacker implements MPType.Unpacker<MPNil> { | ||||||
|  |  | ||||||
|  |         public boolean is(int firstByte) { | ||||||
|  |             return firstByte == NIL; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public MPNil unpack(int firstByte, InputStream in) { | ||||||
|  |             if (firstByte != NIL) { | ||||||
|  |                 throw new IllegalArgumentException(String.format("0xC0 expected but was 0x%02x", firstByte)); | ||||||
|  |             } | ||||||
|  |             return new MPNil(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										185
									
								
								src/main/java/ch/dissem/msgpack/types/MPString.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								src/main/java/ch/dissem/msgpack/types/MPString.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,185 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack.types; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.nio.ByteBuffer; | ||||||
|  | import java.nio.charset.Charset; | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
|  | import static ch.dissem.msgpack.types.Utils.bytes; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Representation of a msgpack encoded string. The encoding is automatically selected according to the string's length. | ||||||
|  |  * <p> | ||||||
|  |  * The default encoding is UTF-8. | ||||||
|  |  * </p> | ||||||
|  |  */ | ||||||
|  | public class MPString implements MPType<String>, CharSequence { | ||||||
|  |     private static final int FIXSTR_PREFIX = 0b10100000; | ||||||
|  |     private static final int FIXSTR_PREFIX_FILTER = 0b11100000; | ||||||
|  |     private static final int STR8_PREFIX = 0xD9; | ||||||
|  |     private static final int STR8_LIMIT = 256; | ||||||
|  |     private static final int STR16_PREFIX = 0xDA; | ||||||
|  |     private static final int STR16_LIMIT = 65536; | ||||||
|  |     private static final int STR32_PREFIX = 0xDB; | ||||||
|  |     private static final int FIXSTR_FILTER = 0b00011111; | ||||||
|  |  | ||||||
|  |     private static Charset encoding = Charset.forName("UTF-8"); | ||||||
|  |  | ||||||
|  |     private final String value; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Use this method if for some messed up reason you really need to use something else than UTF-8. | ||||||
|  |      * Ask yourself: why should I? Is this really necessary? | ||||||
|  |      * <p> | ||||||
|  |      * It will set the encoding for all {@link MPString}s, but if you have inconsistent encoding in your | ||||||
|  |      * format you're lost anyway. | ||||||
|  |      * </p> | ||||||
|  |      */ | ||||||
|  |     public static void setEncoding(Charset encoding) { | ||||||
|  |         MPString.encoding = encoding; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public MPString(String value) { | ||||||
|  |         this.value = value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public String getValue() { | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void pack(OutputStream out) throws IOException { | ||||||
|  |         byte[] bytes = value.getBytes(encoding); | ||||||
|  |         int size = bytes.length; | ||||||
|  |         if (size < 32) { | ||||||
|  |             out.write(FIXSTR_PREFIX + size); | ||||||
|  |         } else if (size < STR8_LIMIT) { | ||||||
|  |             out.write(STR8_PREFIX); | ||||||
|  |             out.write(size); | ||||||
|  |         } else if (size < STR16_LIMIT) { | ||||||
|  |             out.write(STR16_PREFIX); | ||||||
|  |             out.write(ByteBuffer.allocate(2).putShort((short) size).array()); | ||||||
|  |         } else { | ||||||
|  |             out.write(STR32_PREFIX); | ||||||
|  |             out.write(ByteBuffer.allocate(4).putInt(size).array()); | ||||||
|  |         } | ||||||
|  |         out.write(bytes); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean equals(Object o) { | ||||||
|  |         if (this == o) return true; | ||||||
|  |         if (o == null || getClass() != o.getClass()) return false; | ||||||
|  |         MPString mpString = (MPString) o; | ||||||
|  |         return Objects.equals(value, mpString.value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         return Objects.hash(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int length() { | ||||||
|  |         return value.length(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public char charAt(int i) { | ||||||
|  |         return value.charAt(i); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public CharSequence subSequence(int beginIndex, int endIndex) { | ||||||
|  |         return value.subSequence(beginIndex, endIndex); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toJson() { | ||||||
|  |         StringBuilder result = new StringBuilder(value.length() + 4); | ||||||
|  |         result.append('"'); | ||||||
|  |         for (int i = 0; i < value.length(); i++) { | ||||||
|  |             char c = value.charAt(i); | ||||||
|  |             switch (c) { | ||||||
|  |                 case '\\': | ||||||
|  |                 case '"': | ||||||
|  |                 case '/': | ||||||
|  |                     result.append('\\').append(c); | ||||||
|  |                     break; | ||||||
|  |                 case '\b': | ||||||
|  |                     result.append("\\b"); | ||||||
|  |                     break; | ||||||
|  |                 case '\t': | ||||||
|  |                     result.append("\\t"); | ||||||
|  |                     break; | ||||||
|  |                 case '\n': | ||||||
|  |                     result.append("\\n"); | ||||||
|  |                     break; | ||||||
|  |                 case '\f': | ||||||
|  |                     result.append("\\f"); | ||||||
|  |                     break; | ||||||
|  |                 case '\r': | ||||||
|  |                     result.append("\\r"); | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     if (c < ' ') { | ||||||
|  |                         result.append("\\u"); | ||||||
|  |                         String hex = Integer.toHexString(c); | ||||||
|  |                         for (int j = 0; j + hex.length() < 4; j++) { | ||||||
|  |                             result.append('0'); | ||||||
|  |                         } | ||||||
|  |                         result.append(hex); | ||||||
|  |                     } else { | ||||||
|  |                         result.append(c); | ||||||
|  |                     } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         result.append('"'); | ||||||
|  |         return result.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Unpacker implements MPType.Unpacker<MPString> { | ||||||
|  |         public boolean is(int firstByte) { | ||||||
|  |             return firstByte == STR8_PREFIX || firstByte == STR16_PREFIX || firstByte == STR32_PREFIX | ||||||
|  |                     || (firstByte & FIXSTR_PREFIX_FILTER) == FIXSTR_PREFIX; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public MPString unpack(int firstByte, InputStream in) throws IOException { | ||||||
|  |             int size; | ||||||
|  |             if ((firstByte & FIXSTR_PREFIX_FILTER) == FIXSTR_PREFIX) { | ||||||
|  |                 size = firstByte & FIXSTR_FILTER; | ||||||
|  |             } else if (firstByte == STR8_PREFIX) { | ||||||
|  |                 size = in.read(); | ||||||
|  |             } else if (firstByte == STR16_PREFIX) { | ||||||
|  |                 size = in.read() << 8 | in.read(); | ||||||
|  |             } else if (firstByte == STR32_PREFIX) { | ||||||
|  |                 size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read(); | ||||||
|  |             } else { | ||||||
|  |                 throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte)); | ||||||
|  |             } | ||||||
|  |             return new MPString(new String(bytes(in, size).array(), encoding)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								src/main/java/ch/dissem/msgpack/types/MPType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/main/java/ch/dissem/msgpack/types/MPType.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack.types; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Representation of some msgpack encoded data. | ||||||
|  |  */ | ||||||
|  | public interface MPType<T> { | ||||||
|  |     interface Unpacker<M extends MPType> { | ||||||
|  |         boolean is(int firstByte); | ||||||
|  |  | ||||||
|  |         M unpack(int firstByte, InputStream in) throws IOException; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     T getValue(); | ||||||
|  |  | ||||||
|  |     void pack(OutputStream out) throws IOException; | ||||||
|  |  | ||||||
|  |     String toJson(); | ||||||
|  | } | ||||||
							
								
								
									
										115
									
								
								src/main/java/ch/dissem/msgpack/types/Utils.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/main/java/ch/dissem/msgpack/types/Utils.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack.types; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.nio.ByteBuffer; | ||||||
|  |  | ||||||
|  | public class Utils { | ||||||
|  |     private static final char[] BASE64_CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray(); | ||||||
|  |     private static final MPNil NIL = new MPNil(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns a {@link ByteBuffer} containing the next <code>count</code> bytes from the {@link InputStream}. | ||||||
|  |      */ | ||||||
|  |     static ByteBuffer bytes(InputStream in, int count) throws IOException { | ||||||
|  |         byte[] result = new byte[count]; | ||||||
|  |         int off = 0; | ||||||
|  |         while (off < count) { | ||||||
|  |             int read = in.read(result, off, count - off); | ||||||
|  |             if (read < 0) { | ||||||
|  |                 throw new IOException("Unexpected end of stream, wanted to read " + count + " bytes but only got " + off); | ||||||
|  |             } | ||||||
|  |             off += read; | ||||||
|  |         } | ||||||
|  |         return ByteBuffer.wrap(result); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Helper method to decide which types support extra indention (for pretty printing JSON) | ||||||
|  |      */ | ||||||
|  |     static String toJson(MPType<?> type, String indent) { | ||||||
|  |         if (type instanceof MPMap) { | ||||||
|  |             return ((MPMap) type).toJson(indent); | ||||||
|  |         } | ||||||
|  |         if (type instanceof MPArray) { | ||||||
|  |             return ((MPArray) type).toJson(indent); | ||||||
|  |         } | ||||||
|  |         return type.toJson(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Slightly improved code from https://en.wikipedia.org/wiki/Base64 | ||||||
|  |      */ | ||||||
|  |     static String base64(byte[] data) { | ||||||
|  |         StringBuilder result = new StringBuilder((data.length * 4) / 3 + 3); | ||||||
|  |         int b; | ||||||
|  |         for (int i = 0; i < data.length; i += 3) { | ||||||
|  |             b = (data[i] & 0xFC) >> 2; | ||||||
|  |             result.append(BASE64_CODES[b]); | ||||||
|  |             b = (data[i] & 0x03) << 4; | ||||||
|  |             if (i + 1 < data.length) { | ||||||
|  |                 b |= (data[i + 1] & 0xF0) >> 4; | ||||||
|  |                 result.append(BASE64_CODES[b]); | ||||||
|  |                 b = (data[i + 1] & 0x0F) << 2; | ||||||
|  |                 if (i + 2 < data.length) { | ||||||
|  |                     b |= (data[i + 2] & 0xC0) >> 6; | ||||||
|  |                     result.append(BASE64_CODES[b]); | ||||||
|  |                     b = data[i + 2] & 0x3F; | ||||||
|  |                     result.append(BASE64_CODES[b]); | ||||||
|  |                 } else { | ||||||
|  |                     result.append(BASE64_CODES[b]); | ||||||
|  |                     result.append('='); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 result.append(BASE64_CODES[b]); | ||||||
|  |                 result.append("=="); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return result.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static MPString mp(String value) { | ||||||
|  |         return new MPString(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static MPBoolean mp(boolean value) { | ||||||
|  |         return new MPBoolean(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static MPFloat mp(double value) { | ||||||
|  |         return new MPFloat(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static MPFloat mp(float value) { | ||||||
|  |         return new MPFloat(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static MPInteger mp(long value) { | ||||||
|  |         return new MPInteger(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static MPBinary mp(byte... data) { | ||||||
|  |         return new MPBinary(data); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static MPNil nil() { | ||||||
|  |         return NIL; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										270
									
								
								src/test/java/ch/dissem/msgpack/ReaderTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								src/test/java/ch/dissem/msgpack/ReaderTest.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,270 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2017 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.msgpack; | ||||||
|  |  | ||||||
|  | import ch.dissem.msgpack.types.*; | ||||||
|  | import org.junit.Test; | ||||||
|  |  | ||||||
|  | import java.io.ByteArrayInputStream; | ||||||
|  | import java.io.ByteArrayOutputStream; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Random; | ||||||
|  |  | ||||||
|  | import static ch.dissem.msgpack.types.Utils.mp; | ||||||
|  | import static ch.dissem.msgpack.types.Utils.nil; | ||||||
|  | import static org.hamcrest.CoreMatchers.instanceOf; | ||||||
|  | import static org.hamcrest.core.Is.is; | ||||||
|  | import static org.junit.Assert.assertThat; | ||||||
|  |  | ||||||
|  | public class ReaderTest { | ||||||
|  |     private static final Random RANDOM = new Random(); | ||||||
|  |     private Reader reader = Reader.getInstance(); | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void ensureDemoJsonIsParsedCorrectly() throws Exception { | ||||||
|  |         MPType read = reader.read(stream("demo.mp")); | ||||||
|  |         assertThat(read, instanceOf(MPMap.class)); | ||||||
|  |         assertThat(read.toString(), is(string("demo.json"))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void ensureDemoJsonIsEncodedCorrectly() throws Exception { | ||||||
|  |         @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") | ||||||
|  |         MPMap<MPString, MPType<?>> object = new MPMap<>(); | ||||||
|  |         object.put(mp("compact"), mp(true)); | ||||||
|  |         object.put(mp("schema"), mp(0)); | ||||||
|  |         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||
|  |         object.pack(out); | ||||||
|  |         assertThat(out.toByteArray(), is(bytes("demo.mp"))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @SuppressWarnings("unchecked") | ||||||
|  |     public void ensureMPArrayIsEncodedAndDecodedCorrectly() throws Exception { | ||||||
|  |         MPArray<MPType<?>> array = new MPArray<>( | ||||||
|  |                 mp(new byte[]{1, 3, 3, 7}), | ||||||
|  |                 mp(false), | ||||||
|  |                 mp(Math.PI), | ||||||
|  |                 mp(1.5f), | ||||||
|  |                 mp(42), | ||||||
|  |                 new MPMap<MPNil, MPNil>(), | ||||||
|  |                 nil(), | ||||||
|  |                 mp("yay! \uD83E\uDD13") | ||||||
|  |         ); | ||||||
|  |         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||
|  |         array.pack(out); | ||||||
|  |         MPType read = reader.read(new ByteArrayInputStream(out.toByteArray())); | ||||||
|  |         assertThat(read, instanceOf(MPArray.class)); | ||||||
|  |         assertThat((MPArray<MPType<?>>) read, is(array)); | ||||||
|  |         assertThat(read.toJson(), is("[\n  AQMDBw==,\n  false,\n  3.141592653589793,\n  1.5,\n  42,\n  {\n  },\n  null,\n  \"yay! 🤓\"\n]")); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void ensureFloatIsEncodedAndDecodedCorrectly() throws Exception { | ||||||
|  |         MPFloat expected = new MPFloat(1.5f); | ||||||
|  |         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||
|  |         expected.pack(out); | ||||||
|  |         MPType read = reader.read(new ByteArrayInputStream(out.toByteArray())); | ||||||
|  |         assertThat(read, instanceOf(MPFloat.class)); | ||||||
|  |         MPFloat actual = (MPFloat) read; | ||||||
|  |         assertThat(actual, is(expected)); | ||||||
|  |         assertThat(actual.getPrecision(), is(MPFloat.Precision.FLOAT32)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void ensureDoubleIsEncodedAndDecodedCorrectly() throws Exception { | ||||||
|  |         MPFloat expected = new MPFloat(Math.PI); | ||||||
|  |         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||
|  |         expected.pack(out); | ||||||
|  |         MPType read = reader.read(new ByteArrayInputStream(out.toByteArray())); | ||||||
|  |         assertThat(read, instanceOf(MPFloat.class)); | ||||||
|  |         MPFloat actual = (MPFloat) read; | ||||||
|  |         assertThat(actual, is(expected)); | ||||||
|  |         assertThat(actual.getValue(), is(Math.PI)); | ||||||
|  |         assertThat(actual.getPrecision(), is(MPFloat.Precision.FLOAT64)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void ensureLongsAreEncodedAndDecodedCorrectly() throws Exception { | ||||||
|  |         // positive fixnum | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(0, 1); | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(127, 1); | ||||||
|  |         // negative fixnum | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(-1, 1); | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(-32, 1); | ||||||
|  |         // uint 8 | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(128, 2); | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(255, 2); | ||||||
|  |         // uint 16 | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(256, 3); | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(65535, 3); | ||||||
|  |         // uint 32 | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(65536, 5); | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(4294967295L, 5); | ||||||
|  |         // uint 64 | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(4294967296L, 9); | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(Long.MAX_VALUE, 9); | ||||||
|  |         // int 8 | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(-33, 2); | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(-128, 2); | ||||||
|  |         // int 16 | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(-129, 3); | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(-32768, 3); | ||||||
|  |         // int 32 | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(-32769, 5); | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(Integer.MIN_VALUE, 5); | ||||||
|  |         // int 64 | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(-2147483649L, 9); | ||||||
|  |         ensureLongIsEncodedAndDecodedCorrectly(Long.MIN_VALUE, 9); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void ensureLongIsEncodedAndDecodedCorrectly(long val, int bytes) throws Exception { | ||||||
|  |         MPInteger value = mp(val); | ||||||
|  |         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||
|  |         value.pack(out); | ||||||
|  |         MPType read = reader.read(new ByteArrayInputStream(out.toByteArray())); | ||||||
|  |         assertThat(out.size(), is(bytes)); | ||||||
|  |         assertThat(read, instanceOf(MPInteger.class)); | ||||||
|  |         assertThat((MPInteger) read, is(value)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void ensureStringsAreEncodedAndDecodedCorrectly() throws Exception { | ||||||
|  |         ensureStringIsEncodedAndDecodedCorrectly(0); | ||||||
|  |         ensureStringIsEncodedAndDecodedCorrectly(31); | ||||||
|  |         ensureStringIsEncodedAndDecodedCorrectly(32); | ||||||
|  |         ensureStringIsEncodedAndDecodedCorrectly(255); | ||||||
|  |         ensureStringIsEncodedAndDecodedCorrectly(256); | ||||||
|  |         ensureStringIsEncodedAndDecodedCorrectly(65535); | ||||||
|  |         ensureStringIsEncodedAndDecodedCorrectly(65536); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void ensureJsonStringsAreEscapedCorrectly() throws Exception { | ||||||
|  |         StringBuilder builder = new StringBuilder(); | ||||||
|  |         for (char c = '\u0001'; c < ' '; c++) { | ||||||
|  |             builder.append(c); | ||||||
|  |         } | ||||||
|  |         MPString string = new MPString(builder.toString()); | ||||||
|  |         assertThat(string.toJson(), is("\"\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f\"")); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void ensureStringIsEncodedAndDecodedCorrectly(int length) throws Exception { | ||||||
|  |         MPString value = new MPString(stringWithLength(length)); | ||||||
|  |         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||
|  |         value.pack(out); | ||||||
|  |         MPType read = reader.read(new ByteArrayInputStream(out.toByteArray())); | ||||||
|  |         assertThat(read, instanceOf(MPString.class)); | ||||||
|  |         assertThat((MPString) read, is(value)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void ensureBinariesAreEncodedAndDecodedCorrectly() throws Exception { | ||||||
|  |         ensureBinaryIsEncodedAndDecodedCorrectly(0); | ||||||
|  |         ensureBinaryIsEncodedAndDecodedCorrectly(255); | ||||||
|  |         ensureBinaryIsEncodedAndDecodedCorrectly(256); | ||||||
|  |         ensureBinaryIsEncodedAndDecodedCorrectly(65535); | ||||||
|  |         ensureBinaryIsEncodedAndDecodedCorrectly(65536); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void ensureBinaryIsEncodedAndDecodedCorrectly(int length) throws Exception { | ||||||
|  |         MPBinary value = new MPBinary(new byte[length]); | ||||||
|  |         RANDOM.nextBytes(value.getValue()); | ||||||
|  |         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||
|  |         value.pack(out); | ||||||
|  |         MPType read = reader.read(new ByteArrayInputStream(out.toByteArray())); | ||||||
|  |         assertThat(read, instanceOf(MPBinary.class)); | ||||||
|  |         assertThat((MPBinary) read, is(value)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void ensureArraysAreEncodedAndDecodedCorrectly() throws Exception { | ||||||
|  |         ensureArrayIsEncodedAndDecodedCorrectly(0); | ||||||
|  |         ensureArrayIsEncodedAndDecodedCorrectly(15); | ||||||
|  |         ensureArrayIsEncodedAndDecodedCorrectly(16); | ||||||
|  |         ensureArrayIsEncodedAndDecodedCorrectly(65535); | ||||||
|  |         ensureArrayIsEncodedAndDecodedCorrectly(65536); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @SuppressWarnings("unchecked") | ||||||
|  |     private void ensureArrayIsEncodedAndDecodedCorrectly(int length) throws Exception { | ||||||
|  |         MPNil nil = new MPNil(); | ||||||
|  |         ArrayList<MPNil> list = new ArrayList<>(length); | ||||||
|  |         for (int i = 0; i < length; i++) { | ||||||
|  |             list.add(nil); | ||||||
|  |         } | ||||||
|  |         MPArray<MPNil> value = new MPArray<>(list); | ||||||
|  |         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||
|  |         value.pack(out); | ||||||
|  |         MPType read = reader.read(new ByteArrayInputStream(out.toByteArray())); | ||||||
|  |         assertThat(read, instanceOf(MPArray.class)); | ||||||
|  |         assertThat((MPArray<MPNil>) read, is(value)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void ensureMapsAreEncodedAndDecodedCorrectly() throws Exception { | ||||||
|  |         ensureMapIsEncodedAndDecodedCorrectly(0); | ||||||
|  |         ensureMapIsEncodedAndDecodedCorrectly(15); | ||||||
|  |         ensureMapIsEncodedAndDecodedCorrectly(16); | ||||||
|  |         ensureMapIsEncodedAndDecodedCorrectly(65535); | ||||||
|  |         ensureMapIsEncodedAndDecodedCorrectly(65536); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @SuppressWarnings("unchecked") | ||||||
|  |     private void ensureMapIsEncodedAndDecodedCorrectly(int size) throws Exception { | ||||||
|  |         MPNil nil = new MPNil(); | ||||||
|  |         HashMap<MPInteger, MPNil> map = new HashMap<>(size); | ||||||
|  |         for (int i = 0; i < size; i++) { | ||||||
|  |             map.put(mp(i), nil); | ||||||
|  |         } | ||||||
|  |         MPMap<MPInteger, MPNil> value = new MPMap<>(map); | ||||||
|  |         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||
|  |         value.pack(out); | ||||||
|  |         MPType read = reader.read(new ByteArrayInputStream(out.toByteArray())); | ||||||
|  |         assertThat(read, instanceOf(MPMap.class)); | ||||||
|  |         assertThat((MPMap<MPInteger, MPNil>) read, is(value)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private String stringWithLength(int length) { | ||||||
|  |         StringBuilder result = new StringBuilder(length); | ||||||
|  |         for (int i = 0; i < length; i++) { | ||||||
|  |             result.append('a'); | ||||||
|  |         } | ||||||
|  |         return result.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private InputStream stream(String resource) { | ||||||
|  |         return getClass().getClassLoader().getResourceAsStream(resource); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private byte[] bytes(String resource) throws IOException { | ||||||
|  |         InputStream in = stream(resource); | ||||||
|  |         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||
|  |         byte[] buffer = new byte[100]; | ||||||
|  |         for (int size = in.read(buffer); size >= 0; size = in.read(buffer)) { | ||||||
|  |             out.write(buffer, 0, size); | ||||||
|  |         } | ||||||
|  |         return out.toByteArray(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private String string(String resource) throws IOException { | ||||||
|  |         return new String(bytes(resource), "UTF-8"); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								src/test/resources/demo.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/test/resources/demo.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | { | ||||||
|  |   "compact": true, | ||||||
|  |   "schema": 0 | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								src/test/resources/demo.mp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/test/resources/demo.mp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user