├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
├── main
└── java
│ └── com
│ └── github
│ └── artbits
│ └── quickio
│ ├── annotations
│ └── Index.java
│ ├── api
│ ├── Collection.java
│ ├── FindOptions.java
│ ├── JDB.java
│ ├── JKV.java
│ └── JTin.java
│ ├── core
│ ├── Codec.java
│ ├── Config.java
│ ├── Constants.java
│ ├── EngineIO.java
│ ├── EngineLevel.java
│ ├── IOEntity.java
│ ├── Indexer.java
│ ├── JsonObject.java
│ ├── Plugin.java
│ ├── QCollection.java
│ ├── QDB.java
│ ├── QFindOptions.java
│ ├── QKV.java
│ ├── QTin.java
│ ├── QuickIO.java
│ ├── ReflectObject.java
│ └── Snowflake.java
│ ├── exception
│ └── QIOException.java
│ └── struct
│ └── Document.java
└── test
└── java
└── apis
├── DBExample.java
├── KVExample.java
└── TinExample.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.war
15 | *.nar
16 | *.ear
17 | *.zip
18 | *.tar.gz
19 | *.rar
20 |
21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
22 | hs_err_pid*
23 |
24 | # Gradle files
25 | .gradle/
26 | build/
27 |
28 | # IntelliJ
29 | *.iml
30 | .idea/
31 |
32 | # Folder
33 | database/
34 | data/
35 | out/
36 | META-INF
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.jitpack.io/#artbits/quickio)
2 | [](https://jdk.java.net/)
3 | [](#license)
4 |
5 |
6 | ## QuickIO
7 | QuickIO is a Java embedded database. The underlying layer is based on the ``LevelDB`` engine and Java NIO design, and uses ``Protostaff`` to serialize/deserialize data. Support the storage of document, key-value and file type data. Directly using Java code to operate databases is simple, flexible, and efficient.
8 |
9 |
10 | ## Features
11 | + Embedded databases like ``SQLite`` do not need to be installed or independent processes.
12 | + NoSQL databases like ``MongoDB`` or ``Diskv`` are very simple to use.
13 | + Support the storage of document, key-value and file type data.
14 | + Unique index is supported to meet the requirement of fast query.
15 | + Simple API, elegant operation using Java Lambda expressions.
16 | + Fast reading and writing to meet the use scenarios of small and medium-sized data.
17 |
18 |
19 | ## Download
20 | Gradle:
21 | ```groovy
22 | repositories {
23 | maven { url 'https://www.jitpack.io' }
24 | }
25 |
26 | dependencies {
27 | implementation 'com.github.artbits:quickio:1.4.1'
28 | }
29 | ```
30 | Maven:
31 | ```xml
32 |
33 | jitpack.io
34 | https://www.jitpack.io
35 |
36 |
37 |
38 | com.github.artbits
39 | quickio
40 | 1.4.1
41 |
42 | ```
43 |
44 |
45 | ## Usage
46 | Store data of document type.
47 | ```java
48 | JDB db = QuickIO.db("example_db");
49 | Collection collection = db.collection(Document.class);
50 |
51 | collection.save(new Document().put("city", "Canton").put("area", 7434.4));
52 |
53 | Document document = collection.findOne(d -> "Canton".equals(d.get("city")));
54 | Optional.ofNullable(document).ifPresent(IOEntity::printJson);
55 | ```
56 | Custom entity classes are stored according to the data of document type.
57 | ```java
58 | public class Book extends IOEntity {
59 | public String name;
60 | public String author;
61 | public Double price;
62 |
63 | public static Book of(Consumer consumer) {
64 | Book book = new Book();
65 | consumer.accept(book);
66 | return book;
67 | }
68 | }
69 |
70 |
71 | JDB db = QuickIO.db("example_db");
72 | Collection collection = db.collection(Book.class);
73 |
74 | collection.save(Book.of(b -> {
75 | b.name = "On java 8";
76 | b.author = "Bruce Eckel";
77 | b.price = 129.8;
78 | }));
79 |
80 | List books = collection.findAll();
81 | books.forEach(IOEntity::printJson);
82 | ```
83 | Store data of Key-Value type, and support any key and value that can be serialized and deserialized.
84 | ```java
85 | JKV kv = QuickIO.kv("example_kv");
86 | kv.set("Pi", 3.14);
87 | kv.set(3.14, "Pi");
88 |
89 | double d = kv.get("Pi", Double.class);
90 | String s = kv.get(3.14, String.class);
91 | QuickIO.println("%s = %f", s, d);
92 | ```
93 | Stores data for file types.
94 | ```java
95 | JTin tin = QuickIO.tin("example_tin");
96 | tin.put("photo.png", new File("..."));
97 |
98 | File file = tin.get("photo.png");
99 | Optional.ofNullable(file).ifPresent(f -> QuickIO.println(f.getPath()));
100 | ```
101 |
102 |
103 | ## Links
104 | + APIs:
105 | + [DB - Document storage](/src/test/java/apis/DBExample.java)
106 | + [KV - Key-Value storage](/src/test/java/apis/KVExample.java)
107 | + [Tin - File storage](/src/test/java/apis/TinExample.java)
108 | + Thanks:
109 | + [LevelDB](https://github.com/dain/leveldb)
110 | + [LevelDB-Beta](https://github.com/artbits/leveldb-beta)
111 | + [Protostuff](https://github.com/protostuff/protostuff)
112 |
113 |
114 | ## License
115 | ```
116 | Copyright 2022 Zhang Guanhu
117 |
118 | Licensed under the Apache License, Version 2.0 (the "License");
119 | you may not use this file except in compliance with the License.
120 | You may obtain a copy of the License at
121 |
122 | http://www.apache.org/licenses/LICENSE-2.0
123 |
124 | Unless required by applicable law or agreed to in writing, software
125 | distributed under the License is distributed on an "AS IS" BASIS,
126 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
127 | See the License for the specific language governing permissions and
128 | limitations under the License.
129 | ```
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'maven-publish'
4 | }
5 |
6 | group 'com.github.artbits'
7 | version '0.0.1'
8 |
9 | repositories {
10 | maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
11 | maven { url 'https://www.jitpack.io' }
12 | }
13 |
14 | dependencies {
15 | // implementation 'org.iq80.leveldb:leveldb:0.12'
16 | implementation 'com.github.artbits:leveldb-beta:1.0.0'
17 | implementation 'io.protostuff:protostuff-core:1.8.0'
18 | implementation 'io.protostuff:protostuff-runtime:1.8.0'
19 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
20 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
21 | }
22 |
23 | test {
24 | useJUnitPlatform()
25 | }
26 |
27 | java {
28 | withSourcesJar()
29 | withJavadocJar()
30 | }
31 |
32 | publishing {
33 | publications {
34 | maven(MavenPublication) {
35 | from components.java
36 | }
37 | }
38 | }
39 |
40 | wrapper {
41 | gradleVersion = "7.4"
42 | distributionType = Wrapper.DistributionType.ALL
43 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/artbits/quickio/20abbef9208605624001b882560a60f89613e85f/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'quickio'
2 |
3 |
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/annotations/Index.java:
--------------------------------------------------------------------------------
1 | package com.github.artbits.quickio.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target(ElementType.FIELD)
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface Index {
11 |
12 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/api/Collection.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.api;
18 |
19 | import com.github.artbits.quickio.core.IOEntity;
20 |
21 | import java.math.BigDecimal;
22 | import java.util.List;
23 | import java.util.function.Consumer;
24 | import java.util.function.Predicate;
25 |
26 | public interface Collection {
27 | void save(final T t);
28 | void save(final List list);
29 | void update(T t , Predicate predicate);
30 | void updateWithIndex(T t, Consumer consumer);
31 | void delete(long id);
32 | void delete(long... ids);
33 | void delete(List ids);
34 | void delete(Predicate predicate);
35 | void deleteAll();
36 | void deleteWithIndex(Consumer consumer);
37 | List findAll();
38 | List find(Predicate predicate, Consumer consumer);
39 | List find(Predicate predicate);
40 | List find(List ids);
41 | List find(long... ids);
42 | List findWithID(Predicate predicate, Consumer consumer);
43 | List findWithID(Predicate predicate);
44 | List findWithTime(Predicate predicate, Consumer consumer);
45 | List findWithTime(Predicate predicate);
46 | T findFirst(Predicate predicate);
47 | T findFirst();
48 | T findLast(Predicate predicate);
49 | T findLast();
50 | T findOne(long id);
51 | T findOne(Predicate predicate);
52 | T findWithIndex(Consumer consumer);
53 | boolean exist(Consumer consumer);
54 | void dropIndex(String fieldName);
55 | long count(Predicate predicate);
56 | long count();
57 | BigDecimal sum(String fieldName, Predicate predicate);
58 | BigDecimal sum(String fieldName);
59 | Double average(String fieldName, Predicate predicate);
60 | Double average(String fieldName);
61 | BigDecimal max(String fieldName, Predicate predicate);
62 | BigDecimal max(String fieldName);
63 | BigDecimal min(String fieldName, Predicate predicate);
64 | BigDecimal min(String fieldName);
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/api/FindOptions.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.api;
18 |
19 | public interface FindOptions {
20 | FindOptions sort(String fieldName, int value);
21 | FindOptions skip(long size);
22 | FindOptions limit(long size);
23 | void index(String fieldName, Object fieldValue);
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/api/JDB.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.api;
18 |
19 | import com.github.artbits.quickio.core.IOEntity;
20 |
21 | public interface JDB extends AutoCloseable {
22 | @Override
23 | void close();
24 | void destroy();
25 | Collection collection(Class clazz);
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/api/JKV.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.api;
18 |
19 | import com.github.artbits.quickio.core.Config;
20 |
21 | import java.util.function.BiConsumer;
22 | import java.util.function.BiFunction;
23 |
24 | public interface JKV extends AutoCloseable {
25 | @Override
26 | void close();
27 | void destroy();
28 | void set(K key, V value);
29 | V get(K key, V defaultValue);
30 | V get(K key, Class clazz);
31 | boolean del(K key);
32 | boolean exists(K key);
33 | void rename(K oldKey, K newKey);
34 | String type(K key);
35 | void foreach(Class kClass, Class vClass, BiConsumer consumer);
36 | void foreach(Class kClass, Class vClass, BiFunction function);
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/api/JTin.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.api;
18 |
19 | import java.io.File;
20 | import java.util.List;
21 | import java.util.function.Predicate;
22 |
23 | public interface JTin extends AutoCloseable {
24 | @Override
25 | void close();
26 | void destroy();
27 | void put(String filename, File file);
28 | void put(String filename, String url);
29 | void put(String filename, byte[] bytes);
30 | File get(String filename);
31 | void remove(String filename);
32 | void rename(String source, String target);
33 | List list();
34 | void foreach(Predicate predicate);
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/Codec.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import io.protostuff.LinkedBuffer;
20 | import io.protostuff.ProtobufIOUtil;
21 | import io.protostuff.Schema;
22 | import io.protostuff.runtime.RuntimeSchema;
23 |
24 | import java.nio.ByteBuffer;
25 | import java.nio.charset.StandardCharsets;
26 | import java.util.concurrent.ConcurrentHashMap;
27 |
28 | final class Codec {
29 |
30 | private final static ConcurrentHashMap, byte[]> map = new ConcurrentHashMap<>();
31 |
32 |
33 | static byte[] encodeKey(long v) {
34 | return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(v).array();
35 | }
36 |
37 |
38 | static long decodeKey(byte[] bytes) {
39 | ByteBuffer buffer = ByteBuffer.allocate(8);
40 | buffer.put(bytes, 0, bytes.length);
41 | buffer.flip();
42 | return buffer.getLong();
43 | }
44 |
45 |
46 | @SuppressWarnings("unchecked")
47 | static byte[] encode(T t) {
48 | byte[] bytes1 = getClassNameBytes(t.getClass());
49 | byte[] bytes2 = new byte[]{0};
50 | byte[] bytes3 = ProtobufIOUtil.toByteArray(t,
51 | RuntimeSchema.getSchema((Class) t.getClass()),
52 | LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
53 | return ByteBuffer.allocate(bytes1.length + bytes2.length + bytes3.length)
54 | .put(bytes1)
55 | .put(bytes2)
56 | .put(bytes3)
57 | .array();
58 | }
59 |
60 |
61 | static T decode(byte[] bytes, Class clazz) {
62 | byte[] classNameBytes = getClassNameBytes(clazz);
63 | if (bytes == null || bytes.length <= classNameBytes.length) {
64 | return null;
65 | }
66 | ByteBuffer buffer = ByteBuffer.wrap(bytes);
67 | byte[] bytes1 = new byte[classNameBytes.length];
68 | byte[] bytes2 = new byte[]{0};
69 | buffer.get(bytes1, 0, bytes1.length);
70 | buffer.get(bytes2, 0, bytes2.length);
71 | if (bytes2[0] != 0) {
72 | return null;
73 | }
74 | if (!arraysEquals(bytes1, classNameBytes)) {
75 | return null;
76 | }
77 | byte[] bytes3 = new byte[bytes.length - bytes1.length - bytes2.length];
78 | buffer.get(bytes3, 0, bytes3.length);
79 | Schema tSchema = RuntimeSchema.getSchema(clazz);
80 | T t = tSchema.newMessage();
81 | ProtobufIOUtil.mergeFrom(bytes3, t, tSchema);
82 | return t;
83 | }
84 |
85 |
86 | static T clone(T t, Class clazz) {
87 | Schema schema = RuntimeSchema.getSchema(clazz);
88 | byte[] bytes = ProtobufIOUtil.toByteArray(t, schema, LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
89 | T t1 = schema.newMessage();
90 | ProtobufIOUtil.mergeFrom(bytes, t1, schema);
91 | return t1;
92 | }
93 |
94 |
95 | static String getClassName(byte[] bytes) {
96 | StringBuilder stringBuilder = new StringBuilder();
97 | for (byte b : bytes) {
98 | if (b == 0) {
99 | break;
100 | } else {
101 | stringBuilder.append((char) (int) b);
102 | }
103 | }
104 | return stringBuilder.toString();
105 | }
106 |
107 |
108 | private static byte[] getClassNameBytes(Class> clazz) {
109 | byte[] bytes = map.getOrDefault(clazz, null);
110 | if (bytes == null) {
111 | bytes = clazz.getSimpleName().getBytes(StandardCharsets.UTF_8);
112 | map.put(clazz, bytes);
113 | }
114 | return bytes;
115 | }
116 |
117 |
118 | private static boolean arraysEquals(byte[] a1, byte[] a2) {
119 | if (a1 == a2) {
120 | return true;
121 | }
122 | if (a1 == null || a2 == null) {
123 | return false;
124 | }
125 | int length = a1.length;
126 | if (a2.length != length) {
127 | return false;
128 | }
129 | for (int i = 0, size = length / 2; i <= size; i++) {
130 | if (a1[i] != a2[i] || a1[length - 1 - i] != a2[length - 1 - i]) {
131 | return false;
132 | }
133 | }
134 | return true;
135 | }
136 |
137 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/Config.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import java.util.function.Consumer;
20 |
21 | public final class Config {
22 |
23 | String name;
24 | String path;
25 | Long cacheSize;
26 |
27 |
28 | private Config() { }
29 |
30 |
31 | public Config name(String name) {
32 | this.name = name;
33 | return this;
34 | }
35 |
36 |
37 | public Config path(String path) {
38 | this.path = path;
39 | return this;
40 | }
41 |
42 |
43 | public Config cache(Long size) {
44 | this.cacheSize = size;
45 | return this;
46 | }
47 |
48 |
49 | public static Config of(Consumer consumer) {
50 | Config config = new Config();
51 | consumer.accept(config);
52 | return config;
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/Constants.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | final class Constants {
20 | final static String DB_PATH = "data/db/";
21 | final static String KV_PATH = "data/kv/";
22 | final static String TIN_PATH = "data/tin/";
23 | final static String INDEX = "index";
24 |
25 | final static String ILLEGAL_NAME = "The name cannot be null or empty";
26 | final static String SPECIAL_CHARACTER_NAME = "The name cannot contain \"/\"";
27 | final static String INDEX_ALREADY_EXISTS = " index already exists";
28 | final static String NON_INDEXED_FIELD = "Non indexed field";
29 | final static String FIELD_DOES_NOT_EXIST = "This field does not exist";
30 | final static String FIELD_DOES_NOT_SUPPORT_SORTING = "This field does not support sorting";
31 | final static String SORTING_FIELD_NAME_ILLEGAL = "The sort method field name cannot be null or empty";
32 | final static String SORTING_PARAMETER_VALUE_ILLEGAL = "The sorting parameter value can only be 1 or -1";
33 | final static String KEY_ALREADY_EXISTS_AND_NOT_AVAILABLE = "The new key already exists and is not available";
34 | final static String FIELD_NOT_NUMERICAL_TYPE = "This field is not of numerical type";
35 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/EngineIO.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import org.iq80.leveldb.api.WriteBatch;
20 |
21 | import java.util.function.BiConsumer;
22 | import java.util.function.BiFunction;
23 | import java.util.function.Consumer;
24 |
25 | interface EngineIO extends AutoCloseable {
26 | EngineIO open(Config config);
27 | @Override
28 | void close();
29 | void destroy();
30 | void put(byte[] key, byte[] value);
31 | void delete(byte[] key);
32 | byte[] get(byte[] key);
33 | void writeBatch(Consumer consumer);
34 | void iteration(BiConsumer consumer);
35 | T iteration(BiFunction function);
36 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/EngineLevel.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import com.github.artbits.quickio.exception.QIOException;
20 | import org.iq80.leveldb.api.*;
21 | import org.iq80.leveldb.impl.Iq80DBFactory;
22 |
23 | import java.io.File;
24 | import java.io.IOException;
25 | import java.nio.file.Files;
26 | import java.nio.file.Path;
27 | import java.nio.file.Paths;
28 | import java.util.Comparator;
29 | import java.util.function.BiConsumer;
30 | import java.util.function.BiFunction;
31 | import java.util.function.Consumer;
32 |
33 | final class EngineLevel implements EngineIO {
34 |
35 | private File file;
36 | private DBFactory factory;
37 | private DB db;
38 |
39 |
40 | @Override
41 | public EngineIO open(Config config) {
42 | if (config.name == null || config.name.isEmpty()) {
43 | throw new QIOException(Constants.ILLEGAL_NAME);
44 | } else if (config.name.contains("/")) {
45 | throw new QIOException(Constants.SPECIAL_CHARACTER_NAME);
46 | }
47 | if (config.cacheSize == null || config.cacheSize <= 0) {
48 | config.cacheSize = 10L * 1024 * 1024;
49 | }
50 | try {
51 | file = Paths.get(config.path, config.name).toFile();
52 | factory = new Iq80DBFactory();
53 | db = factory.open(file, new Options().createIfMissing(true).cacheSize(config.cacheSize));
54 | } catch (IOException e) {
55 | throw new QIOException(e);
56 | }
57 | return this;
58 | }
59 |
60 |
61 | @Override
62 | public void close() {
63 | try {
64 | if (db != null) {
65 | db.close();
66 | db = null;
67 | }
68 | } catch (IOException e) {
69 | throw new QIOException(e);
70 | }
71 | }
72 |
73 |
74 | @Override
75 | public void destroy() {
76 | try {
77 | factory.destroy(file, null);
78 | close();
79 | Path filePath = Paths.get(file.getPath());
80 | Comparator comparator = Comparator.reverseOrder();
81 | Files.walk(filePath).sorted(comparator).forEach(path -> {
82 | try {
83 | Files.delete(path);
84 | } catch (IOException e) {
85 | throw new QIOException(e);
86 | }
87 | });
88 | } catch (IOException e) {
89 | throw new QIOException(e);
90 | }
91 | }
92 |
93 |
94 | @Override
95 | public void put(byte[] key, byte[] value) {
96 | db.put(key, value);
97 | }
98 |
99 |
100 | @Override
101 | public void delete(byte[] key) {
102 | db.delete(key);
103 | }
104 |
105 |
106 | @Override
107 | public byte[] get(byte[] key) {
108 | return db.get(key);
109 | }
110 |
111 |
112 | @Override
113 | public void writeBatch(Consumer consumer) {
114 | try (WriteBatch batch = db.createWriteBatch()) {
115 | consumer.accept(batch);
116 | db.write(batch);
117 | } catch (IOException e) {
118 | throw new QIOException(e);
119 | }
120 | }
121 |
122 |
123 | @Override
124 | public void iteration(BiConsumer consumer) {
125 | try (DBIterator iterator = db.iterator()) {
126 | for(iterator.seekToFirst(); iterator.hasNext(); iterator.next()) {
127 | byte[] key = iterator.peekNext().getKey();
128 | byte[] value = iterator.peekNext().getValue();
129 | consumer.accept(key, value);
130 | }
131 | } catch (IOException e) {
132 | throw new QIOException(e);
133 | }
134 | }
135 |
136 |
137 | @Override
138 | public T iteration(BiFunction function) {
139 | try (DBIterator iterator = db.iterator()) {
140 | for(iterator.seekToFirst(); iterator.hasNext(); iterator.next()) {
141 | byte[] key = iterator.peekNext().getKey();
142 | byte[] value = iterator.peekNext().getValue();
143 | T t = function.apply(key, value);
144 | if (t != null) {
145 | return t;
146 | }
147 | }
148 | return null;
149 | } catch (IOException e) {
150 | throw new QIOException(e);
151 | }
152 | }
153 |
154 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/IOEntity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | public class IOEntity {
20 |
21 | long _id;
22 | long createdAt;
23 |
24 |
25 | public final long objectId() {
26 | return _id;
27 | }
28 |
29 |
30 | public final long createdAt() {
31 | return createdAt;
32 | }
33 |
34 |
35 | public final String toJson() {
36 | return Plugin.toJson(this);
37 | }
38 |
39 |
40 | public final void printJson() {
41 | Plugin.printJson(this);
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/Indexer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import com.github.artbits.quickio.annotations.Index;
20 | import com.github.artbits.quickio.exception.QIOException;
21 | import org.iq80.leveldb.api.WriteBatch;
22 |
23 | import java.util.*;
24 | import java.util.concurrent.atomic.AtomicReference;
25 | import java.util.stream.Collectors;
26 |
27 | import static com.github.artbits.quickio.core.Constants.INDEX;
28 |
29 | final class Indexer {
30 |
31 | private final EngineIO engine;
32 |
33 |
34 | private static class IndexObject {
35 | String className;
36 | String filedName;
37 | Object value;
38 |
39 | IndexObject(String className, String filedName, Object value) {
40 | this.className = className;
41 | this.filedName = filedName;
42 | this.value = value;
43 | }
44 |
45 | @Override
46 | public String toString() {
47 | return "[" + className + "," + filedName + "," + value + "]";
48 | }
49 | }
50 |
51 |
52 | private static class IndexMapObject {
53 | Map indexMap = new HashMap<>();
54 | }
55 |
56 |
57 | Indexer(EngineIO engine, String path, String name) {
58 | this.engine = engine.open(Config.of(c -> c.name(INDEX)
59 | .path(path + "/" + name)
60 | .cache(1024 * 1024L)));
61 | }
62 |
63 |
64 | void close() {
65 | engine.close();
66 | }
67 |
68 |
69 | void destroy() {
70 | engine.destroy();
71 | }
72 |
73 |
74 | private void setIndex(WriteBatch batch, T t) {
75 | List indexObjects = extractIndexObjects(t);
76 | if (indexObjects.size() == 0) return;
77 | long value1 = t.objectId();
78 | long key2 = t.objectId();
79 | byte[] valueBytes2 = engine.get(Codec.encodeKey(key2));
80 | AtomicReference atomicReference = new AtomicReference<>();
81 | if (valueBytes2 != null) {
82 | IndexMapObject indexMapObject = Codec.decode(valueBytes2, IndexMapObject.class);
83 | atomicReference.set(indexMapObject);
84 | }
85 | if (atomicReference.get() == null) {
86 | atomicReference.set(new IndexMapObject());
87 | }
88 | IndexMapObject value2 = atomicReference.get();
89 | indexObjects.forEach(indexObject -> {
90 | String key1 = indexObject.toString();
91 | String oldKey1 = value2.indexMap.getOrDefault(indexObject.filedName, null);
92 | if (oldKey1 == null) {
93 | value2.indexMap.put(indexObject.filedName, key1);
94 | batch.put(Codec.encode(key1), Codec.encodeKey(value1));
95 | }
96 | if (!key1.equals(oldKey1)) {
97 | value2.indexMap.put(indexObject.filedName, key1);
98 | batch.put(Codec.encode(key1), Codec.encodeKey(value1));
99 | Optional.ofNullable(oldKey1).ifPresent(key -> batch.delete(Codec.encode(key)));
100 | }
101 | });
102 | batch.put(Codec.encodeKey(key2), Codec.encode(value2));
103 | }
104 |
105 |
106 | void setIndex(T t) {
107 | engine.writeBatch(batch -> setIndex(batch, t));
108 | }
109 |
110 |
111 | void setIndexes(List list) {
112 | if (list.size() < 1) {
113 | return;
114 | }
115 | if (!new ReflectObject<>(list.get(0)).containsAnnotation(Index.class)) {
116 | return;
117 | }
118 | Map guardMap = new HashMap<>();
119 | list.forEach(t -> {
120 | List indexObjects = extractIndexObjects(t);
121 | indexObjects.forEach(indexObject -> {
122 | String key1 = indexObject.toString();
123 | if (guardMap.getOrDefault(key1, false)) {
124 | throw new QIOException(key1 + Constants.INDEX_ALREADY_EXISTS);
125 | } else {
126 | guardMap.put(key1, true);
127 | }
128 | });
129 | });
130 | engine.writeBatch(batch -> list.forEach(t -> setIndex(batch, t)));
131 | }
132 |
133 |
134 | void removeIndex(long id) {
135 | if (id == 0) return;
136 | byte[] valueBytes2 = engine.get(Codec.encodeKey(id));
137 | if (valueBytes2 != null) {
138 | engine.writeBatch(batch -> {
139 | IndexMapObject indexMapObject = Codec.decode(valueBytes2, IndexMapObject.class);
140 | indexMapObject.indexMap.values().forEach(key1 -> batch.delete(Codec.encode(key1)));
141 | batch.delete(Codec.encodeKey(id));
142 | });
143 | }
144 | }
145 |
146 |
147 | void removeIndex(T t) {
148 | removeIndex(t.objectId());
149 | }
150 |
151 |
152 | void removeIndexes(long... ids) {
153 | engine.writeBatch(batch -> {
154 | for (long id : ids) {
155 | byte[] valueBytes2 = engine.get(Codec.encodeKey(id));
156 | if (valueBytes2 != null) {
157 | IndexMapObject value2 = Codec.decode(valueBytes2, IndexMapObject.class);
158 | value2.indexMap.values().forEach(key1 -> batch.delete(Codec.encode(key1)));
159 | batch.delete(Codec.encodeKey(id));
160 | }
161 | }
162 | });
163 | }
164 |
165 |
166 | void removeIndexes(List ids) {
167 | long[] idArray = new long[ids.size()];
168 | for (int i = 0, len = ids.size(); i < len; i++) {
169 | idArray[i] = ids.get(i);
170 | }
171 | removeIndexes(idArray);
172 | }
173 |
174 |
175 | void removeIndexList(List list) {
176 | List ids = list.stream().map(IOEntity::objectId).filter(id -> id != 0).collect(Collectors.toList());
177 | removeIndexes(ids);
178 | }
179 |
180 |
181 | void dropIndex(List list, String fieldName) {
182 | engine.writeBatch(batch -> list.forEach(t -> {
183 | byte[] valueBytes2 = engine.get(Codec.encode(t.objectId()));
184 | if (valueBytes2 != null) {
185 | IndexMapObject value2 = Codec.decode(valueBytes2, IndexMapObject.class);
186 | String key1 = value2.indexMap.getOrDefault(fieldName, null);
187 | Optional.ofNullable(key1).ifPresent(key -> batch.delete(Codec.encode(key)));
188 | }
189 | }));
190 | }
191 |
192 |
193 | long getIndexId(Class> tClass, String fieldName, Object filedValue) {
194 | try {
195 | if (tClass.getDeclaredField(fieldName).isAnnotationPresent(Index.class)) {
196 | IndexObject indexObject = new IndexObject(tClass.getSimpleName(), fieldName, filedValue);
197 | String key1 = indexObject.toString();
198 | byte[] keyBytes1 = Codec.encode(key1);
199 | byte[] valueBytes1 = engine.get(keyBytes1);
200 | return (valueBytes1 != null) ? Codec.decodeKey(valueBytes1) : 0;
201 | } else {
202 | throw new QIOException(Constants.NON_INDEXED_FIELD);
203 | }
204 | } catch (NoSuchFieldException e) {
205 | throw new QIOException(Constants.NON_INDEXED_FIELD);
206 | }
207 | }
208 |
209 |
210 | boolean exist(Class> tClass, String fieldName, Object filedValue) {
211 | return getIndexId(tClass, fieldName, filedValue) != 0;
212 | }
213 |
214 |
215 | private List extractIndexObjects(T t) {
216 | String className = t.getClass().getSimpleName();
217 | List indexObjects = new ArrayList<>();
218 | ReflectObject reflectObject = new ReflectObject<>(t);
219 | reflectObject.traverseAnnotationFields(Index.class, (fieldName, fieldValue) -> {
220 | if (fieldValue == null) {
221 | return;
222 | }
223 | IndexObject indexObject = new IndexObject(className, fieldName, fieldValue);
224 | String key1 = indexObject.toString();
225 | byte[] valueBytes1 = engine.get(Codec.encode(key1));
226 | if (valueBytes1 != null) {
227 | long value1 = Codec.decodeKey(valueBytes1);
228 | if (value1 != t.objectId()) {
229 | throw new QIOException(key1 + Constants.INDEX_ALREADY_EXISTS);
230 | }
231 | }
232 | indexObjects.add(indexObject);
233 | });
234 | return indexObjects;
235 | }
236 |
237 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/JsonObject.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import java.lang.reflect.Field;
20 | import java.math.BigDecimal;
21 | import java.math.BigInteger;
22 | import java.util.*;
23 |
24 | final class JsonObject {
25 |
26 | private final Map map = new LinkedHashMap<>();
27 |
28 |
29 | JsonObject(T t) {
30 | beanToMap(t);
31 | }
32 |
33 |
34 | private void beanToMap(T t) {
35 | try {
36 | map.put("\"_id\"", null);
37 | Class> clazz = t.getClass();
38 | while (clazz != null) {
39 | for (Field field : clazz.getDeclaredFields()) {
40 | field.setAccessible(true);
41 | putMap(field.getName(), field.get(t));
42 | }
43 | clazz = clazz.getSuperclass();
44 | }
45 | } catch (IllegalAccessException e) {
46 | throw new RuntimeException(e);
47 | }
48 | }
49 |
50 |
51 | private void putMap(String fieldName, Object fieldValue) {
52 | if (fieldName == null || fieldValue == null || fieldValue instanceof Enum>) {
53 | return;
54 | } else {
55 | fieldName = "\"" + fieldName + "\"";
56 | }
57 |
58 | if (fieldValue instanceof String || fieldValue instanceof Character) {
59 | if (!"\u0000".equals(String.valueOf(fieldValue))) {
60 | map.put(fieldName, "\"" + fieldValue + "\"");
61 | }
62 | return;
63 | }
64 | if (fieldValue instanceof Byte || fieldValue instanceof Short
65 | || fieldValue instanceof Integer || fieldValue instanceof Long
66 | || fieldValue instanceof Boolean || fieldValue instanceof Float
67 | || fieldValue instanceof Double || fieldValue instanceof BigInteger
68 | || fieldValue instanceof BigDecimal) {
69 | map.put(fieldName, String.valueOf(fieldValue));
70 | return;
71 | }
72 | if (fieldValue.getClass().isArray()) {
73 | map.put(fieldName, arrayToJSONString(fieldValue));
74 | return;
75 | }
76 | if (fieldValue instanceof Collection>) {
77 | map.put(fieldName, collectionToJSONString((Collection>) fieldValue));
78 | return;
79 | }
80 | if (fieldValue instanceof Map, ?>) {
81 | map.put(fieldName, mapToJSONString((Map, ?>) fieldValue));
82 | return;
83 | }
84 | map.put(fieldName, new JsonObject(fieldValue).toString());
85 | }
86 |
87 |
88 | @Override
89 | public String toString() {
90 | StringBuilder builder = new StringBuilder().append("{");
91 | map.forEach((k, v) -> Optional.ofNullable(v).ifPresent(s -> builder.append(k).append(":").append(v).append(",")));
92 | return ((builder.length() > 2) ? builder.deleteCharAt(builder.length() - 1) : builder).append("}").toString();
93 | }
94 |
95 |
96 | private static String charArrayToJSONString(char[] chars) {
97 | StringBuilder builder = new StringBuilder();
98 | builder.append("[");
99 | for (char c : chars) {
100 | builder.append("\"").append(c).append("\"").append(",");
101 | }
102 | return ((builder.length() > 2) ? builder.deleteCharAt(builder.length() - 1) : builder).append("]").toString();
103 | }
104 |
105 |
106 | private static String objectArrayToJSONString(T[] arrays) {
107 | StringBuilder builder = new StringBuilder();
108 | builder.append("[");
109 | for (Object o : arrays) {
110 | ifTextType(o, () -> {
111 | builder.append("\"").append(o).append("\"").append(",");
112 | }, () -> {
113 | builder.append(new JsonObject(o)).append(",");
114 | });
115 | }
116 | return ((builder.length() > 2) ? builder.deleteCharAt(builder.length() - 1) : builder).append("]").toString();
117 | }
118 |
119 |
120 | private static String arrayToJSONString(Object object) {
121 | if (object instanceof byte[]) {
122 | return Arrays.toString((byte[]) object).replaceAll(" ", "");
123 | }
124 | if (object instanceof short[]) {
125 | return Arrays.toString((short[]) object).replaceAll(" ", "");
126 | }
127 | if (object instanceof int[]) {
128 | return Arrays.toString((int[]) object).replaceAll(" ", "");
129 | }
130 | if (object instanceof long[]) {
131 | return Arrays.toString((long[]) object).replaceAll(" ", "");
132 | }
133 | if (object instanceof float[]) {
134 | return Arrays.toString((float[]) object).replaceAll(" ", "");
135 | }
136 | if (object instanceof double[]) {
137 | return Arrays.toString((double[]) object).replaceAll(" ", "");
138 | }
139 | if (object instanceof boolean[]) {
140 | return Arrays.toString((boolean[]) object);
141 | }
142 | if (object instanceof char[]) {
143 | return charArrayToJSONString((char[]) object);
144 | }
145 | if (object instanceof Object[]) {
146 | return objectArrayToJSONString((Object[]) object);
147 | }
148 | return null;
149 | }
150 |
151 |
152 | private static String collectionToJSONString(Collection> collection) {
153 | StringBuilder builder = new StringBuilder();
154 | builder.append("[");
155 | collection.forEach(o -> {
156 | ifBasicType(o, () -> {
157 | builder.append(o).append(",");
158 | }, () -> {
159 | ifTextType(o, () -> {
160 | builder.append("\"").append(o).append("\"").append(",");
161 | }, () -> {
162 | builder.append(new JsonObject(o)).append(",");
163 | });
164 | });
165 | });
166 | return ((builder.length() > 2) ? builder.deleteCharAt(builder.length() - 1) : builder).append("]").toString();
167 | }
168 |
169 |
170 | private static String mapToJSONString(Map, ?> map) {
171 | StringBuilder builder = new StringBuilder();
172 | builder.append("{");
173 | map.forEach((k, v) -> Optional.ofNullable((k != null && v != null) ? k : null).ifPresent(o -> {
174 | builder.append("\"").append(k).append("\":");
175 | ifBasicType(v, () -> {
176 | builder.append(v).append(",");
177 | }, () -> {
178 | ifTextType(v, () -> {
179 | builder.append("\"").append(v).append("\"").append(",");
180 | }, () -> {
181 | builder.append(new JsonObject(v)).append(",");
182 | });
183 | });
184 | }));
185 | return ((builder.length() > 2) ? builder.deleteCharAt(builder.length() - 1) : builder).append("}").toString();
186 | }
187 |
188 |
189 | private static void ifBasicType(Object o, Runnable runnable1, Runnable runnable2) {
190 | if (o instanceof Byte || o instanceof Short || o instanceof Integer
191 | || o instanceof Long || o instanceof Boolean || o instanceof Float
192 | || o instanceof Double || o instanceof BigInteger || o instanceof BigDecimal) {
193 | Optional.ofNullable(runnable1).ifPresent(Runnable::run);
194 | } else {
195 | Optional.ofNullable(runnable2).ifPresent(Runnable::run);
196 | }
197 | }
198 |
199 |
200 | private static void ifTextType(Object o, Runnable runnable1, Runnable runnable2) {
201 | if (o instanceof String || o instanceof Character) {
202 | Optional.ofNullable(runnable1).ifPresent(Runnable::run);
203 | } else {
204 | Optional.ofNullable(runnable2).ifPresent(Runnable::run);
205 | }
206 | }
207 |
208 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/Plugin.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import java.time.Instant;
20 | import java.time.ZoneId;
21 | import java.time.format.DateTimeFormatter;
22 | import java.util.Optional;
23 | import java.util.function.Predicate;
24 |
25 | class Plugin {
26 |
27 | private final static Snowflake snowflake = new Snowflake(0, 0);
28 |
29 |
30 | static int getDigit(long i){
31 | i = i > 0 ? i : -i;
32 | return i == 0 ? 1 : (int) Math.log10(i) + 1;
33 | }
34 |
35 |
36 | public static long generateId() {
37 | return snowflake.nextId();
38 | }
39 |
40 |
41 | public static long toTimestamp(long id) {
42 | return snowflake.toTimestamp(id);
43 | }
44 |
45 |
46 | public static String toDateTime(long timestamp) {
47 | return Instant.ofEpochMilli(timestamp)
48 | .atZone(ZoneId.systemDefault())
49 | .toLocalDateTime()
50 | .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
51 | }
52 |
53 |
54 | public static String toJson(T t) {
55 | return new JsonObject(t).toString();
56 | }
57 |
58 |
59 | public static void printJson(T t) {
60 | System.out.println(Optional.ofNullable(t)
61 | .map(s -> toJson(t))
62 | .orElse("The converted JSON object cannot be null"));
63 | }
64 |
65 |
66 | public static void print(Object o) {
67 | System.out.print(o);
68 | }
69 |
70 |
71 | public static void print(String s, Object... args) {
72 | System.out.printf(s, args);
73 | }
74 |
75 |
76 | public static void println(Object o) {
77 | System.out.println(o);
78 | }
79 |
80 |
81 | public static void println(String s, Object... args) {
82 | System.out.printf(s + "%n", args);
83 | }
84 |
85 |
86 | public static byte[] encode(T t) {
87 | return Codec.encode(t);
88 | }
89 |
90 |
91 | public static T decode(byte[] bytes, Class clazz) {
92 | return Codec.decode(bytes, clazz);
93 | }
94 |
95 |
96 | public static boolean bool(T value, Predicate predicate) {
97 | return value != null && predicate.test(value);
98 | }
99 |
100 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/QCollection.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import com.github.artbits.quickio.api.Collection;
20 | import com.github.artbits.quickio.api.FindOptions;
21 | import com.github.artbits.quickio.exception.QIOException;
22 | import org.iq80.leveldb.api.DBException;
23 |
24 | import java.math.BigDecimal;
25 | import java.util.ArrayList;
26 | import java.util.List;
27 | import java.util.Optional;
28 | import java.util.concurrent.atomic.AtomicLong;
29 | import java.util.concurrent.atomic.AtomicReference;
30 | import java.util.function.Consumer;
31 | import java.util.function.Predicate;
32 |
33 |
34 | final class QCollection implements Collection {
35 |
36 | private final EngineIO engine;
37 | private final Indexer indexer;
38 | private final Class clazz;
39 |
40 |
41 | QCollection(Class clazz, EngineIO engine, Indexer indexer) {
42 | this.clazz = clazz;
43 | this.engine = engine;
44 | this.indexer = indexer;
45 | }
46 |
47 |
48 | @Override
49 | public void save(T t) {
50 | if (t.objectId() == 0 || Plugin.getDigit(t.objectId()) < 18) {
51 | t._id = Plugin.generateId();
52 | t.createdAt = Plugin.toTimestamp(t.objectId());
53 | }
54 | indexer.setIndex(t);
55 | try {
56 | engine.put(Codec.encodeKey(t.objectId()), Codec.encode(t));
57 | } catch (DBException e) {
58 | indexer.removeIndex(t);
59 | throw new QIOException(e);
60 | }
61 | }
62 |
63 |
64 | @Override
65 | public void save(List list) {
66 | list.forEach(t -> {
67 | if (t.objectId() == 0 || Plugin.getDigit(t.objectId()) < 18) {
68 | t._id = Plugin.generateId();
69 | t.createdAt = Plugin.toTimestamp(t.objectId());
70 | }
71 | });
72 | indexer.setIndexes(list);
73 | try {
74 | engine.writeBatch(batch -> list.forEach(t -> batch.put(Codec.encodeKey(t.objectId()), Codec.encode(t))));
75 | } catch (Exception e) {
76 | indexer.removeIndexList(list);
77 | throw new QIOException(e);
78 | }
79 | }
80 |
81 |
82 | @Override
83 | public void update(T t, Predicate predicate) {
84 | List newLocalTList = new ArrayList<>();
85 | List oldLocalTList = new ArrayList<>();
86 | ReflectObject tObject = new ReflectObject<>(t);
87 | engine.iteration((key, value) -> {
88 | T localT = Codec.decode(value, clazz);
89 | if (localT != null && predicate.test(localT)) {
90 | oldLocalTList.add(Codec.clone(localT, clazz));
91 | ReflectObject object = new ReflectObject<>(localT);
92 | tObject.traverseFields((name, value1) -> Optional.ofNullable(value1).ifPresent(v -> object.setValue(name, v)));
93 | newLocalTList.add(object.get());
94 | }
95 | });
96 | indexer.setIndexes(newLocalTList);
97 | try {
98 | engine.writeBatch(batch -> newLocalTList.forEach(t1 -> batch.put(Codec.encodeKey(t1.objectId()), Codec.encode(t1))));
99 | } catch (Exception e) {
100 | indexer.removeIndexList(newLocalTList);
101 | indexer.setIndexes(oldLocalTList);
102 | throw new QIOException(e);
103 | }
104 | }
105 |
106 |
107 | @Override
108 | public void updateWithIndex(T t, Consumer consumer) {
109 | T localT = findWithIndex(consumer);
110 | if (localT != null) {
111 | ReflectObject object = new ReflectObject<>(localT);
112 | ReflectObject tObject = new ReflectObject<>(t);
113 | tObject.traverseFields((name, value) -> Optional.ofNullable(value).ifPresent(v -> object.setValue(name, v)));
114 | save(object.get());
115 | }
116 | }
117 |
118 |
119 | @Override
120 | public void delete(long id) {
121 | engine.delete(Codec.encodeKey(id));
122 | indexer.removeIndex(id);
123 | }
124 |
125 |
126 | @Override
127 | public void delete(long... ids) {
128 | engine.writeBatch(batch -> {
129 | for (long id : ids) {
130 | batch.delete(Codec.encodeKey(id));
131 | }
132 | });
133 | indexer.removeIndexes(ids);
134 | }
135 |
136 |
137 | @Override
138 | public void delete(List ids) {
139 | engine.writeBatch(batch -> ids.forEach(id -> batch.delete(Codec.encodeKey(id))));
140 | indexer.removeIndexes(ids);
141 | }
142 |
143 |
144 | @Override
145 | public void delete(Predicate predicate) {
146 | List ids = new ArrayList<>();
147 | engine.writeBatch(batch -> engine.iteration((key, value) -> {
148 | T t = Codec.decode(value, clazz);
149 | if (t != null) {
150 | if (predicate != null && !predicate.test(t)) {
151 | return;
152 | }
153 | batch.delete(key);
154 | ids.add(t.objectId());
155 | }
156 | }));
157 | indexer.removeIndexes(ids);
158 | }
159 |
160 |
161 | @Override
162 | public void deleteAll() {
163 | delete((Predicate) null);
164 | }
165 |
166 |
167 | @Override
168 | public void deleteWithIndex(Consumer consumer) {
169 | T t = findWithIndex(consumer);
170 | Optional.ofNullable(t).ifPresent(t1 -> delete(t1.objectId()));
171 | }
172 |
173 |
174 | @Override
175 | public List findAll() {
176 | return find(null, null);
177 | }
178 |
179 |
180 | @Override
181 | public List find(Predicate predicate, Consumer consumer) {
182 | QFindOptions options = (consumer != null) ? new QFindOptions() : null;
183 | Optional.ofNullable(consumer).ifPresent(c -> c.accept(options));
184 | List list = new ArrayList<>();
185 | engine.iteration((key, value) -> {
186 | T t = Codec.decode(value, clazz);
187 | if (t != null) {
188 | if (predicate != null && !predicate.test(t)) {
189 | return;
190 | }
191 | list.add(t);
192 | }
193 | });
194 | return (consumer != null) ? options.get(list) : list;
195 | }
196 |
197 |
198 | @Override
199 | public List find(Predicate predicate) {
200 | return find(predicate, null);
201 | }
202 |
203 |
204 | @Override
205 | public List find(List ids) {
206 | List list = new ArrayList<>();
207 | ids.forEach(id -> {
208 | byte[] key = Codec.encodeKey(id);
209 | byte[] value = engine.get(key);
210 | T t = (value != null) ? Codec.decode(value, clazz) : null;
211 | Optional.ofNullable(t).ifPresent(list::add);
212 | });
213 | return list;
214 | }
215 |
216 |
217 | @Override
218 | public List find(long... ids) {
219 | List list = new ArrayList<>();
220 | for (long id : ids) {
221 | byte[] key = Codec.encodeKey(id);
222 | byte[] value = engine.get(key);
223 | T t = (value != null) ? Codec.decode(value, clazz) : null;
224 | Optional.ofNullable(t).ifPresent(list::add);
225 | }
226 | return list;
227 | }
228 |
229 |
230 | @Override
231 | public List findWithID(Predicate predicate, Consumer consumer) {
232 | QFindOptions options = (consumer != null) ? new QFindOptions() : null;
233 | Optional.ofNullable(consumer).ifPresent(c -> c.accept(options));
234 | List list = new ArrayList<>();
235 | engine.iteration((key, value) -> {
236 | long id = Codec.decodeKey(key);
237 | if (predicate.test(id)) {
238 | T t = Codec.decode(value, clazz);
239 | Optional.ofNullable(t).ifPresent(list::add);
240 | }
241 | });
242 | return (consumer != null) ? options.get(list) : list;
243 | }
244 |
245 |
246 | @Override
247 | public List findWithID(Predicate predicate) {
248 | return findWithID(predicate, null);
249 | }
250 |
251 |
252 | @Override
253 | public List findWithTime(Predicate predicate, Consumer consumer) {
254 | return findWithID(id -> predicate.test(Plugin.toTimestamp(id)), consumer);
255 | }
256 |
257 |
258 | @Override
259 | public List findWithTime(Predicate predicate) {
260 | return findWithTime(predicate, null);
261 | }
262 |
263 |
264 | @Override
265 | public T findFirst(Predicate predicate) {
266 | AtomicReference minT = new AtomicReference<>();
267 | engine.iteration((key, value) -> {
268 | long id = Codec.decodeKey(key);
269 | T t = Codec.decode(value, clazz);
270 | if (t != null && (minT.get() == null || id < minT.get().objectId())) {
271 | if (predicate != null && !predicate.test(t)) {
272 | return;
273 | }
274 | minT.set(t);
275 | }
276 | });
277 | return minT.get();
278 | }
279 |
280 |
281 | @Override
282 | public T findFirst() {
283 | return findFirst(null);
284 | }
285 |
286 |
287 | @Override
288 | public T findLast(Predicate predicate) {
289 | AtomicReference maxT = new AtomicReference<>();
290 | engine.iteration((key, value) -> {
291 | long id = Codec.decodeKey(key);
292 | T t = Codec.decode(value, clazz);
293 | if (t != null && (maxT.get() == null || id > maxT.get().objectId())) {
294 | if (predicate != null && !predicate.test(t)) {
295 | return;
296 | }
297 | maxT.set(t);
298 | }
299 | });
300 | return maxT.get();
301 | }
302 |
303 |
304 | @Override
305 | public T findLast() {
306 | return findLast(null);
307 | }
308 |
309 |
310 | @Override
311 | public T findOne(long id) {
312 | byte[] key = Codec.encodeKey(id);
313 | byte[] value = engine.get(key);
314 | return (value != null) ? Codec.decode(value, clazz) : null;
315 | }
316 |
317 |
318 | @Override
319 | public T findOne(Predicate predicate) {
320 | return engine.iteration((key, value) -> {
321 | T t = Codec.decode(value, clazz);
322 | return (t != null && predicate.test(t)) ? t : null;
323 | });
324 | }
325 |
326 |
327 | @Override
328 | public T findWithIndex(Consumer consumer) {
329 | QFindOptions options = new QFindOptions();
330 | consumer.accept(options);
331 | long id = indexer.getIndexId(clazz, options.indexName, options.indexValue);
332 | return findOne(id);
333 | }
334 |
335 |
336 | @Override
337 | public boolean exist(Consumer consumer) {
338 | QFindOptions options = new QFindOptions();
339 | consumer.accept(options);
340 | return indexer.exist(clazz, options.indexName, options.indexValue);
341 | }
342 |
343 |
344 | @Override
345 | public void dropIndex(String fieldName) {
346 | List list = findAll();
347 | indexer.dropIndex(list, fieldName);
348 | }
349 |
350 |
351 | @Override
352 | public long count(Predicate predicate) {
353 | AtomicLong count = new AtomicLong(0);
354 | engine.iteration((key, value) -> {
355 | T t = Codec.decode(value, clazz);
356 | if (t != null) {
357 | if (predicate != null && !predicate.test(t)) {
358 | return;
359 | }
360 | count.incrementAndGet();
361 | }
362 | });
363 | return count.get();
364 | }
365 |
366 |
367 | @Override
368 | public long count() {
369 | return count(null);
370 | }
371 |
372 |
373 | @Override
374 | public BigDecimal sum(String fieldName, Predicate predicate) {
375 | AtomicReference reference = new AtomicReference<>(new BigDecimal(0));
376 | engine.iteration((key, value) -> {
377 | T t = Codec.decode(value, clazz);
378 | if (t != null) {
379 | if (predicate != null && !predicate.test(t)) {
380 | return;
381 | }
382 | reference.set(reference.get().add(new ReflectObject<>(t).getBigDecimalValue(fieldName)));
383 | }
384 | });
385 | return reference.get();
386 | }
387 |
388 |
389 | @Override
390 | public BigDecimal sum(String fieldName) {
391 | return sum(fieldName, null);
392 | }
393 |
394 |
395 | @Override
396 | public Double average(String fieldName, Predicate predicate) {
397 | AtomicReference sum = new AtomicReference<>();
398 | AtomicLong count = new AtomicLong(0);
399 | engine.iteration((key, value) -> {
400 | T t = Codec.decode(value, clazz);
401 | if (t != null) {
402 | if (predicate != null && !predicate.test(t)) {
403 | return;
404 | }
405 | if (sum.get() == null) {
406 | sum.set(new ReflectObject<>(t).getBigDecimalValue(fieldName));
407 | } else {
408 | sum.set(sum.get().add(new ReflectObject<>(t).getBigDecimalValue(fieldName)));
409 | }
410 | count.incrementAndGet();
411 | }
412 | });
413 | return sum.get().doubleValue() / count.get();
414 | }
415 |
416 |
417 | @Override
418 | public Double average(String fieldName) {
419 | return average(fieldName, null);
420 | }
421 |
422 |
423 | @Override
424 | public BigDecimal max(String fieldName, Predicate predicate) {
425 | AtomicReference reference = new AtomicReference<>();
426 | engine.iteration((key, value) -> {
427 | T t = Codec.decode(value, clazz);
428 | if (t != null) {
429 | if (predicate != null && !predicate.test(t)) {
430 | return;
431 | }
432 | BigDecimal bigDecimal = new ReflectObject<>(t).getBigDecimalValue(fieldName);
433 | if (reference.get() == null) {
434 | reference.set(bigDecimal);
435 | } else {
436 | if (bigDecimal != null && bigDecimal.compareTo(reference.get()) > 0) {
437 | reference.set(bigDecimal);
438 | }
439 | }
440 | }
441 | });
442 | return reference.get();
443 | }
444 |
445 |
446 | @Override
447 | public BigDecimal max(String fieldName) {
448 | return max(fieldName, null);
449 | }
450 |
451 |
452 | @Override
453 | public BigDecimal min(String fieldName, Predicate predicate) {
454 | AtomicReference reference = new AtomicReference<>();
455 | engine.iteration((key, value) -> {
456 | T t = Codec.decode(value, clazz);
457 | if (t != null) {
458 | if (predicate != null && !predicate.test(t)) {
459 | return;
460 | }
461 | BigDecimal bigDecimal = new ReflectObject<>(t).getBigDecimalValue(fieldName);
462 | if (reference.get() == null) {
463 | reference.set(bigDecimal);
464 | } else {
465 | if (bigDecimal != null && bigDecimal.compareTo(reference.get()) < 0) {
466 | reference.set(bigDecimal);
467 | }
468 | }
469 | }
470 | });
471 | return reference.get();
472 | }
473 |
474 |
475 | @Override
476 | public BigDecimal min(String fieldName) {
477 | return min(fieldName, null);
478 | }
479 |
480 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/QDB.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import com.github.artbits.quickio.api.Collection;
20 | import com.github.artbits.quickio.api.JDB;
21 |
22 | import java.nio.file.Paths;
23 |
24 | import static com.github.artbits.quickio.core.Constants.DB_PATH;
25 |
26 | final class QDB implements JDB {
27 |
28 | private final EngineIO engine;
29 | private final Indexer indexer;
30 |
31 |
32 | QDB(Config config) {
33 | if (config.path == null) {
34 | config.path = DB_PATH;
35 | } else {
36 | config.path = Paths.get(config.path, DB_PATH).toAbsolutePath().toString();
37 | }
38 | engine = new EngineLevel().open(config);
39 | indexer = new Indexer(new EngineLevel(), config.path, config.name);
40 | Runtime.getRuntime().addShutdownHook(new Thread(this::close));
41 | }
42 |
43 |
44 | QDB(String name) {
45 | this(Config.of(c -> c.name(name)));
46 | }
47 |
48 |
49 | @Override
50 | public void close() {
51 | engine.close();
52 | indexer.close();
53 | }
54 |
55 |
56 | @Override
57 | public void destroy() {
58 | engine.destroy();
59 | indexer.destroy();
60 | }
61 |
62 |
63 | @Override
64 | public Collection collection(Class clazz) {
65 | return new QCollection<>(clazz, engine, indexer);
66 | }
67 |
68 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/QFindOptions.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import com.github.artbits.quickio.api.FindOptions;
20 | import com.github.artbits.quickio.exception.QIOException;
21 |
22 | import java.util.Comparator;
23 | import java.util.List;
24 | import java.util.Optional;
25 | import java.util.stream.Collectors;
26 | import java.util.stream.Stream;
27 |
28 | final class QFindOptions implements FindOptions {
29 |
30 | private String sortFieldName;
31 | private long sortValue;
32 | private long skipSize;
33 | private long limitSize;
34 |
35 | String indexName;
36 | Object indexValue;
37 |
38 |
39 | @Override
40 | public FindOptions sort(String fieldName, int value) {
41 | if (fieldName == null || fieldName.isEmpty()) {
42 | throw new QIOException(Constants.SORTING_FIELD_NAME_ILLEGAL);
43 | }
44 | if (value < -1 || value > 1) {
45 | throw new QIOException(Constants.SORTING_PARAMETER_VALUE_ILLEGAL);
46 | }
47 | sortFieldName = fieldName;
48 | sortValue = value;
49 | return this;
50 | }
51 |
52 |
53 | @Override
54 | public FindOptions skip(long size) {
55 | skipSize = size;
56 | return this;
57 | }
58 |
59 |
60 | @Override
61 | public FindOptions limit(long size) {
62 | limitSize = size;
63 | return this;
64 | }
65 |
66 |
67 | @Override
68 | public void index(String fieldName, Object fieldValue) {
69 | indexName = Optional.ofNullable(fieldName).orElseThrow(NullPointerException::new);
70 | indexValue = Optional.ofNullable(fieldValue).orElseThrow(NullPointerException::new);
71 | }
72 |
73 |
74 | List get(List list) {
75 | Stream stream = (list == null || list.isEmpty()) ? null : list.stream();
76 | if (stream == null) {
77 | return list;
78 | }
79 | if (sortValue != 0) {
80 | Comparator comparator = createComparator(list.get(0));
81 | comparator = (sortValue == 1) ? comparator : comparator.reversed();
82 | stream = stream.parallel().sorted(comparator);
83 | stream = stream.sequential();
84 | }
85 | if (skipSize > 0) {
86 | stream = stream.skip(skipSize);
87 | }
88 | if (limitSize > 0) {
89 | stream = stream.limit(limitSize);
90 | }
91 | return stream.collect(Collectors.toList());
92 | }
93 |
94 |
95 | private Comparator createComparator(T object) {
96 | ReflectObject reflectObject = new ReflectObject<>(object);
97 | if (!reflectObject.contains(sortFieldName)) {
98 | throw new QIOException(Constants.FIELD_DOES_NOT_EXIST);
99 | }
100 | switch (reflectObject.getType(sortFieldName).getSimpleName().toLowerCase()) {
101 | case "byte":
102 | case "short":
103 | case "int":
104 | case "integer":
105 | return Comparator.comparingInt(t -> (int) new ReflectObject<>(t).getValue(sortFieldName));
106 | case "long":
107 | return Comparator.comparingLong(t -> (long) new ReflectObject<>(t).getValue(sortFieldName));
108 | case "float":
109 | case "double":
110 | return Comparator.comparingDouble(t -> (double) new ReflectObject<>(t).getValue(sortFieldName));
111 | default:
112 | throw new QIOException(Constants.FIELD_DOES_NOT_SUPPORT_SORTING);
113 | }
114 | }
115 |
116 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/QKV.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import com.github.artbits.quickio.api.JKV;
20 | import com.github.artbits.quickio.exception.QIOException;
21 |
22 | import java.nio.file.Paths;
23 | import java.util.Arrays;
24 | import java.util.Optional;
25 | import java.util.function.BiConsumer;
26 | import java.util.function.BiFunction;
27 |
28 | import static com.github.artbits.quickio.core.Constants.KV_PATH;
29 |
30 | final class QKV implements JKV {
31 |
32 | private final EngineIO engine;
33 |
34 |
35 | QKV(Config config) {
36 | if (config.path == null) {
37 | config.path = KV_PATH;
38 | } else {
39 | config.path = Paths.get(config.path, KV_PATH).toAbsolutePath().toString();
40 | }
41 | engine = new EngineLevel().open(config);
42 | }
43 |
44 |
45 | QKV(String name) {
46 | this(Config.of(c -> c.name(name)));
47 | }
48 |
49 |
50 | @Override
51 | public void close() {
52 | engine.close();
53 | }
54 |
55 |
56 | @Override
57 | public void destroy() {
58 | engine.destroy();
59 | }
60 |
61 |
62 | @Override
63 | public void set(K key, V value) {
64 | engine.put(Codec.encode(key), Codec.encode(value));
65 | }
66 |
67 |
68 | @SuppressWarnings("unchecked")
69 | @Override
70 | public V get(K key, V defaultValue) {
71 | byte[] bytes = engine.get(Codec.encode(key));
72 | if (bytes != null) {
73 | Object object = Codec.decode(bytes, defaultValue.getClass());
74 | return (object == null) ? defaultValue : (V) object;
75 | }
76 | return defaultValue;
77 | }
78 |
79 |
80 | @Override
81 | public V get(K key, Class clazz) {
82 | byte[] bytes = engine.get(Codec.encode(key));
83 | if (bytes != null) {
84 | Object object = Codec.decode(bytes, clazz);
85 | return (object != null) ? clazz.cast(object) : null;
86 | }
87 | return null;
88 | }
89 |
90 |
91 | @Override
92 | public boolean del(K key) {
93 | engine.delete(Codec.encode(key));
94 | return true;
95 | }
96 |
97 |
98 | @Override
99 | public boolean exists(K key) {
100 | byte[] bytes = engine.get(Codec.encode(key));
101 | return bytes != null;
102 | }
103 |
104 |
105 | @Override
106 | public void rename(K oldKey, K newKey) {
107 | byte[] oldKeyBytes = Codec.encode(oldKey);
108 | byte[] newKeyBytes = Codec.encode(newKey);
109 | if (Arrays.equals(oldKeyBytes, newKeyBytes)) {
110 | return;
111 | }
112 | if (engine.get(newKeyBytes) != null) {
113 | throw new QIOException(Constants.KEY_ALREADY_EXISTS_AND_NOT_AVAILABLE);
114 | }
115 | byte[] valueBytes = engine.get(oldKeyBytes);
116 | Optional.ofNullable(valueBytes).ifPresent(bytes -> engine.writeBatch(batch -> {
117 | engine.put(newKeyBytes, valueBytes);
118 | engine.delete(oldKeyBytes);
119 | }));
120 | }
121 |
122 |
123 | @Override
124 | public String type(K key) {
125 | byte[] bytes = engine.get(Codec.encode(key));
126 | return (bytes != null) ? Codec.getClassName(bytes) : null;
127 | }
128 |
129 |
130 | @Override
131 | public void foreach(Class kClass, Class vClass, BiConsumer consumer) {
132 | engine.iteration((k, v) -> {
133 | K key = Codec.decode(k, kClass);
134 | V value = Codec.decode(v, vClass);
135 | if (key != null && value != null) {
136 | consumer.accept(key, value);
137 | }
138 | });
139 | }
140 |
141 |
142 | @Override
143 | public void foreach(Class kClass, Class vClass, BiFunction function) {
144 | engine.iteration((k, v) -> {
145 | K key = Codec.decode(k, kClass);
146 | V value = Codec.decode(v, vClass);
147 | if (key != null && value != null) {
148 | Boolean b = function.apply(key, value);
149 | return b ? null : b;
150 | }
151 | return null;
152 | });
153 | }
154 |
155 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/QTin.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import com.github.artbits.quickio.api.JTin;
20 | import com.github.artbits.quickio.exception.QIOException;
21 |
22 | import java.io.File;
23 | import java.io.FileOutputStream;
24 | import java.io.IOException;
25 | import java.net.URL;
26 | import java.nio.ByteBuffer;
27 | import java.nio.channels.Channels;
28 | import java.nio.channels.FileChannel;
29 | import java.nio.channels.FileLock;
30 | import java.nio.channels.ReadableByteChannel;
31 | import java.nio.file.*;
32 | import java.util.ArrayList;
33 | import java.util.Comparator;
34 | import java.util.List;
35 | import java.util.function.Predicate;
36 |
37 | import static com.github.artbits.quickio.core.Constants.TIN_PATH;
38 |
39 | final class QTin implements JTin {
40 |
41 | private final String LOCK_FILE_NAME = ".LOCK";
42 |
43 | private final String path;
44 | private FileLock lock;
45 | private FileChannel lockChannel;
46 |
47 |
48 | QTin(Config config) {
49 | if (config.name == null || config.name.isEmpty()) {
50 | throw new QIOException(Constants.ILLEGAL_NAME);
51 | } else if (config.name.contains("/")) {
52 | throw new QIOException(Constants.SPECIAL_CHARACTER_NAME);
53 | }
54 | if (config.path == null) {
55 | path = Paths.get(TIN_PATH, config.name).toString();
56 | } else {
57 | path = Paths.get(config.path, TIN_PATH, config.name).toAbsolutePath().toString();
58 | }
59 | try {
60 | Files.createDirectories(Paths.get(path));
61 | if (!new File(path + "/" + LOCK_FILE_NAME).exists()) {
62 | Files.createFile(Paths.get(path, LOCK_FILE_NAME));
63 | }
64 | lockChannel = new FileOutputStream(path + "/" + LOCK_FILE_NAME,true).getChannel();
65 | lock = lockChannel.lock();
66 | Runtime.getRuntime().addShutdownHook(new Thread(this::close));
67 | } catch (IOException e) {
68 | throw new QIOException(e);
69 | }
70 | }
71 |
72 |
73 | QTin(String name) {
74 | this(Config.of(c -> c.name(name)));
75 | }
76 |
77 |
78 | @Override
79 | public void close() {
80 | try {
81 | if (lock != null) {
82 | lock.release();
83 | lock = null;
84 | }
85 | } catch (IOException e) {
86 | throw new QIOException(e);
87 | }
88 | try {
89 | if (lockChannel != null) {
90 | lockChannel.close();
91 | lockChannel = null;
92 | }
93 | } catch (IOException e) {
94 | throw new QIOException(e);
95 | }
96 | }
97 |
98 |
99 | @Override
100 | public void destroy() {
101 | try {
102 | close();
103 | Path filePath = Paths.get(path);
104 | Comparator comparator = Comparator.reverseOrder();
105 | Files.walk(filePath).sorted(comparator).forEach(path -> {
106 | try {
107 | Files.delete(path);
108 | } catch (IOException e) {
109 | throw new QIOException(e);
110 | }
111 | });
112 | } catch (IOException e) {
113 | throw new QIOException(e);
114 | }
115 | }
116 |
117 |
118 | @Override
119 | public void put(String filename, File file) {
120 | if (!LOCK_FILE_NAME.equals(filename)) {
121 | String outPath = path + "/" + filename;
122 | try (FileChannel inChannel = FileChannel.open(Paths.get(file.getPath()), StandardOpenOption.READ);
123 | FileChannel outChannel = FileChannel.open(Paths.get(outPath), StandardOpenOption.READ,
124 | StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
125 | inChannel.transferTo(0, inChannel.size(), outChannel);
126 | } catch (IOException e) {
127 | throw new QIOException(e);
128 | }
129 | }
130 | }
131 |
132 |
133 | @Override
134 | public void put(String filename, String url) {
135 | if (!LOCK_FILE_NAME.equals(filename)) {
136 | String tempFilename = filename + ".dat";
137 | String outPath = path + "/" + tempFilename;
138 | int length = -1;
139 | try (FileOutputStream stream = new FileOutputStream(outPath); FileChannel fileChannel = stream.getChannel()) {
140 | URL netUrl = new URL(url);
141 | length = netUrl.openConnection().getContentLength();
142 | ReadableByteChannel readableByteChannel = Channels.newChannel(netUrl.openStream());
143 | fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
144 | } catch (IOException e) {
145 | throw new RuntimeException(e);
146 | } finally {
147 | File file = get(tempFilename);
148 | if (file != null && file.length() == length) {
149 | remove(filename);
150 | rename(tempFilename, filename);
151 | } else if (file != null) {
152 | remove(tempFilename);
153 | }
154 | }
155 | }
156 | }
157 |
158 |
159 | @Override
160 | public void put(String filename, byte[] bytes) {
161 | if (!LOCK_FILE_NAME.equals(filename)) {
162 | String outPath = path + "/" + filename;
163 | try (FileChannel outChannel = FileChannel.open(Paths.get(outPath), StandardOpenOption.READ,
164 | StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
165 | ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
166 | buffer.put(bytes);
167 | buffer.flip();
168 | while (buffer.hasRemaining()) {
169 | outChannel.write(buffer);
170 | }
171 | } catch (IOException e) {
172 | throw new QIOException(e);
173 | }
174 | }
175 | }
176 |
177 |
178 | @Override
179 | public File get(String filename) {
180 | if (!LOCK_FILE_NAME.equals(filename)) {
181 | File file = new File(path + "/" + filename);
182 | return file.exists() ? file : null;
183 | }
184 | return null;
185 | }
186 |
187 |
188 | @Override
189 | public void remove(String filename) {
190 | try {
191 | if (!LOCK_FILE_NAME.equals(filename)) {
192 | Files.delete(Paths.get(path + "/" + filename));
193 | }
194 | } catch (NoSuchFileException e) {
195 |
196 | } catch (IOException e) {
197 | throw new QIOException(e);
198 | }
199 | }
200 |
201 |
202 | @Override
203 | public void rename(String source, String target) {
204 | try {
205 | Path sourcePath = Paths.get(path + "/" + source);
206 | Path targetPath = sourcePath.resolveSibling(target);
207 | Files.move(sourcePath, targetPath);
208 | } catch (IOException e) {
209 | throw new QIOException(e);
210 | }
211 | }
212 |
213 |
214 | @Override
215 | public List list() {
216 | List fileList = new ArrayList<>();
217 | File[] files = new File(path).listFiles();
218 | for (File file : files) {
219 | if(file.isDirectory()) {
220 | continue;
221 | }
222 | if (LOCK_FILE_NAME.equals(file.getName())) {
223 | continue;
224 | }
225 | fileList.add(file);
226 | }
227 | return fileList;
228 | }
229 |
230 |
231 | @Override
232 | public void foreach(Predicate predicate) {
233 | File[] files = new File(path).listFiles();
234 | for (File file : files) {
235 | if (file.isDirectory()) {
236 | continue;
237 | }
238 | if (LOCK_FILE_NAME.equals(file.getName())) {
239 | continue;
240 | }
241 | if (!predicate.test(file)) {
242 | break;
243 | }
244 | }
245 | }
246 |
247 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/QuickIO.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import com.github.artbits.quickio.api.JDB;
20 | import com.github.artbits.quickio.api.JKV;
21 | import com.github.artbits.quickio.api.JTin;
22 |
23 | public final class QuickIO extends Plugin {
24 |
25 | public static JDB db(String name) {
26 | return new QDB(name);
27 | }
28 |
29 |
30 | public static JDB db(Config config) {
31 | return new QDB(config);
32 | }
33 |
34 |
35 | public static JKV kv(String name) {
36 | return new QKV(name);
37 | }
38 |
39 |
40 | public static JKV kv(Config config) {
41 | return new QKV(config);
42 | }
43 |
44 |
45 | public static JTin tin(String name) {
46 | return new QTin(name);
47 | }
48 |
49 |
50 | public static JTin tin(Config config) {
51 | return new QTin(config);
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/ReflectObject.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import com.github.artbits.quickio.exception.QIOException;
20 |
21 | import java.lang.annotation.Annotation;
22 | import java.lang.reflect.Field;
23 | import java.math.BigDecimal;
24 | import java.util.HashMap;
25 | import java.util.Map;
26 | import java.util.Optional;
27 | import java.util.function.BiConsumer;
28 |
29 | final class ReflectObject {
30 |
31 | private final Map fieldMap = new HashMap<>();
32 | private final T t;
33 | private long id;
34 | private long createdAt;
35 |
36 |
37 | ReflectObject(T t) {
38 | this.t = t;
39 | Class> clazz = t.getClass();
40 | while (clazz != null){
41 | for (Field field : clazz.getDeclaredFields()) {
42 | field.setAccessible(true);
43 | if ("_id".equals(field.getName())) {
44 | id = t.objectId();
45 | continue;
46 | }
47 | if ("createdAt".equals(field.getName())) {
48 | createdAt = t.createdAt();
49 | continue;
50 | }
51 | fieldMap.put(field.getName(), field);
52 | }
53 | clazz = clazz.getSuperclass();
54 | }
55 | }
56 |
57 |
58 | void traverseFields(BiConsumer consumer) {
59 | try {
60 | for (Field field : fieldMap.values()) {
61 | consumer.accept(field.getName(), field.get(t));
62 | }
63 | } catch (IllegalAccessException e) {
64 | throw new RuntimeException(e);
65 | }
66 | }
67 |
68 |
69 | void traverseAnnotationFields(Class extends Annotation> annotationClass, BiConsumer consumer) {
70 | try {
71 | for (Field field : fieldMap.values()) {
72 | if (field.isAnnotationPresent(annotationClass)) {
73 | consumer.accept(field.getName(), field.get(t));
74 | }
75 | }
76 | } catch (IllegalAccessException e) {
77 | throw new RuntimeException(e);
78 | }
79 | }
80 |
81 |
82 | boolean contains(String fieldName) {
83 | return fieldMap.containsKey(fieldName);
84 | }
85 |
86 |
87 | boolean containsAnnotation(Class extends Annotation> annotationClass) {
88 | for (Field field : fieldMap.values()) {
89 | if (field.isAnnotationPresent(annotationClass)) {
90 | return true;
91 | }
92 | }
93 | return false;
94 | }
95 |
96 |
97 | Object getValue(String fieldName) {
98 | try {
99 | Field field = fieldMap.getOrDefault(fieldName, null);
100 | return (field != null) ? field.get(t) : null;
101 | } catch (IllegalAccessException e) {
102 | throw new RuntimeException(e);
103 | }
104 | }
105 |
106 |
107 | void setValue(String fieldName, Object value) {
108 | try {
109 | Field field = fieldMap.getOrDefault(fieldName, null);
110 | if (field != null) {
111 | field.set(t, value);
112 | }
113 | } catch (IllegalAccessException e) {
114 | throw new RuntimeException(e);
115 | }
116 | }
117 |
118 |
119 | Class> getType(String fieldName) {
120 | Field field = fieldMap.getOrDefault(fieldName, null);
121 | return field.getType();
122 | }
123 |
124 |
125 | BigDecimal getBigDecimalValue(String fieldName) {
126 | try {
127 | switch (fieldName) {
128 | case "_id": return BigDecimal.valueOf(id);
129 | case "createdAt": return BigDecimal.valueOf(createdAt);
130 | }
131 | Field field = fieldMap.getOrDefault(fieldName, null);
132 | Optional.ofNullable(field).orElseThrow(() -> new QIOException(Constants.FIELD_DOES_NOT_EXIST));
133 | switch (field.getType().getSimpleName().toLowerCase()) {
134 | case "int":
135 | case "integer": return BigDecimal.valueOf((Integer) field.get(t));
136 | case "byte": return BigDecimal.valueOf((Byte) field.get(t));
137 | case "short": return BigDecimal.valueOf((Short) field.get(t));
138 | case "long": return BigDecimal.valueOf((Long) field.get(t));
139 | case "float": return BigDecimal.valueOf((Float) field.get(t));
140 | case "double": return BigDecimal.valueOf((Double) field.get(t));
141 | default: throw new QIOException(Constants.FIELD_NOT_NUMERICAL_TYPE);
142 | }
143 | } catch (IllegalAccessException e) {
144 | throw new RuntimeException(e);
145 | }
146 | }
147 |
148 |
149 | T get() {
150 | return t;
151 | }
152 |
153 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/core/Snowflake.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.core;
18 |
19 | import java.util.concurrent.locks.ReentrantLock;
20 |
21 | final class Snowflake {
22 |
23 | private final ReentrantLock lock = new ReentrantLock();
24 |
25 | //Start time cutoff (January 1, 2015)
26 | private final static long epoch = 1420041600000L;
27 |
28 | //Number of digits occupied by the machine ID
29 | private final static long workerIdBits = 5L;
30 |
31 | //Number of digits occupied by data ID
32 | private final static long datacenterIdBits = 5L;
33 |
34 | //The maximum machine ID supported is 31
35 | // (this shift algorithm can quickly calculate
36 | // the maximum decimal number represented by
37 | // several binary numbers)
38 | private final static long maxWorkerId = ~(-1L << workerIdBits);
39 |
40 | //The maximum supported data ID is 31
41 | private final static long maxDatacenterId = ~(-1L << datacenterIdBits);
42 |
43 | //Number of digits occupied by the sequence in the ID
44 | private final static long sequenceBits = 12L;
45 |
46 | //Machine ID moves 12 bits to the left
47 | private final static long workerIdShift = sequenceBits;
48 |
49 | //Move the data ID 17 bits to the left (12+5)
50 | private final static long datacenterIdShift = sequenceBits + workerIdBits;
51 |
52 | //Move the time cutoff 22 bits to the left (5+5+12)
53 | private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
54 |
55 | //The mask of the generated sequence, which is 4095 (0b1111111111111=0xfff=4095)
56 | private final static long sequenceMask = ~(-1L << sequenceBits);
57 |
58 | //Working machine ID (0~31)
59 | private final long workerId;
60 |
61 | //Data center ID (0~31)
62 | private final long datacenterId;
63 |
64 | //Sequence in milliseconds (0~4095)
65 | private long sequence = 0L;
66 |
67 | //Last ID generation time
68 | private long lastTimestamp = -1L;
69 |
70 |
71 | //workerId(0 ~ 31), datacenterId(0 ~ 31)
72 | Snowflake(long workerId, long datacenterId) {
73 | if (workerId > maxWorkerId || workerId < 0) {
74 | throw new IllegalArgumentException(String.format("worker Id " +
75 | "can't be greater than %d or less than 0", maxWorkerId));
76 | }
77 | if (datacenterId > maxDatacenterId || datacenterId < 0) {
78 | throw new IllegalArgumentException(String.format("datacenter Id " +
79 | "can't be greater than %d or less than 0", maxDatacenterId));
80 | }
81 | this.workerId = workerId;
82 | this.datacenterId = datacenterId;
83 | }
84 |
85 |
86 | //Block to the next millisecond until a new timestamp is obtained
87 | //lastTimestamp: Last ID generation time
88 | private long tilNextMillis(long lastTimestamp) {
89 | long timestamp = timeGen();
90 | while (timestamp <= lastTimestamp) {
91 | timestamp = timeGen();
92 | }
93 | return timestamp;
94 | }
95 |
96 |
97 | //Returns the current time in milliseconds (milliseconds)
98 | private long timeGen() {
99 | return System.currentTimeMillis();
100 | }
101 |
102 |
103 | //Get the next ID (this method is thread safe)
104 | long nextId() {
105 | try {
106 | lock.lock();
107 | long timestamp = timeGen();
108 | //If the current time is less than the timestamp generated by the last ID,
109 | // it means that the system clock should throw an exception when it goes back
110 | if (timestamp < lastTimestamp) {
111 | throw new RuntimeException(String.format("Clock moved backwards. " +
112 | "Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
113 | }
114 | //If it is generated at the same time, sequence within milliseconds
115 | if (lastTimestamp == timestamp) {
116 | sequence = (sequence + 1) & sequenceMask;
117 | //Sequence overflow in milliseconds
118 | if (sequence == 0) {
119 | //Block to the next millisecond to obtain a new timestamp
120 | timestamp = tilNextMillis(lastTimestamp);
121 | }
122 | }
123 | //Timestamp change, sequence reset within milliseconds
124 | else {
125 | sequence = 0L;
126 | }
127 | //Last ID generation time
128 | lastTimestamp = timestamp;
129 | //The 64-bit ID is formed by shifting and or operation
130 | return ((timestamp - epoch) << timestampLeftShift)
131 | | (datacenterId << datacenterIdShift)
132 | | (workerId << workerIdShift)
133 | | sequence;
134 | } finally {
135 | lock.unlock();
136 | }
137 | }
138 |
139 |
140 | //through id activity timestamp
141 | long toTimestamp(long snowId) {
142 | String id = Long.toBinaryString(snowId);
143 | long timestamp = id.length() - (workerIdBits + datacenterIdBits + sequenceBits);
144 | String s = id.substring(0, (int) timestamp);
145 | return Long.parseUnsignedLong(s, 2) + epoch;
146 | }
147 |
148 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/exception/QIOException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.exception;
18 |
19 | public class QIOException extends RuntimeException {
20 |
21 | public QIOException(String message) {
22 | super(message);
23 | }
24 |
25 |
26 | public QIOException(Throwable cause) {
27 | super(cause);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/github/artbits/quickio/struct/Document.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Zhang Guanhu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.artbits.quickio.struct;
18 |
19 | import com.github.artbits.quickio.core.IOEntity;
20 |
21 | import java.util.HashMap;
22 |
23 | public final class Document extends IOEntity {
24 |
25 | private final HashMap