nosj 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/Cargo.lock +11 -1
- data/README.md +63 -11
- data/ext/nosj/Cargo.toml +4 -1
- data/ext/nosj/src/files.rs +218 -0
- data/ext/nosj/src/gen/mod.rs +44 -10
- data/ext/nosj/src/lazy.rs +402 -0
- data/ext/nosj/src/lib.rs +30 -0
- data/ext/nosj/src/pointer.rs +14 -12
- data/lib/nosj/lazy.rb +165 -0
- data/lib/nosj/version.rb +1 -1
- data/lib/nosj.rb +85 -2
- data/sig/nosj.rbs +54 -0
- metadata +7 -2
data/lib/nosj.rb
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "nosj/version"
|
|
4
4
|
require_relative "nosj/native"
|
|
5
|
+
require_relative "nosj/lazy"
|
|
5
6
|
|
|
6
7
|
# nosj is the evil twin of the +json+ gem: the same API, output bytes,
|
|
7
8
|
# option names, and error messages, backed by the Rust
|
|
8
9
|
# {https://github.com/yaroslav/nosj nosj} crate with SIMD-accelerated
|
|
9
10
|
# parsing and generation. Beyond the +json+ gem's surface it adds
|
|
10
|
-
# zero-allocation validation ({.valid?})
|
|
11
|
-
# ({.dig}, {.at_pointer}, and their batch forms)
|
|
11
|
+
# zero-allocation validation ({.valid?}), partial parsing
|
|
12
|
+
# ({.dig}, {.at_pointer}, and their batch forms), and lazy documents
|
|
13
|
+
# ({.lazy}).
|
|
12
14
|
#
|
|
13
15
|
# Options arrive as a positional Hash (the +json+ gem's own calling
|
|
14
16
|
# convention); an explicit +**kwargs+ would allocate per call.
|
|
@@ -190,4 +192,85 @@ module NOSJ
|
|
|
190
192
|
def self.at_pointers(source, pointers, opts = nil)
|
|
191
193
|
at_pointers_native(source, pointers, opts)
|
|
192
194
|
end
|
|
195
|
+
|
|
196
|
+
# Parses a JSON file, like +JSON.load_file+—except the file is read
|
|
197
|
+
# natively into a reused buffer, so no file-sized Ruby String is ever
|
|
198
|
+
# created (or garbage-collected).
|
|
199
|
+
#
|
|
200
|
+
# @example
|
|
201
|
+
# NOSJ.load_file("config.json", symbolize_names: true)
|
|
202
|
+
#
|
|
203
|
+
# @param path [String] the file to parse (UTF-8)
|
|
204
|
+
# @param opts [Hash, nil] the same options as {.parse}
|
|
205
|
+
# @return [Object] the parsed value tree
|
|
206
|
+
# @raise [SystemCallError] +Errno::ENOENT+ and friends, like File.read
|
|
207
|
+
# @raise [RuntimeError] when the document is malformed or not UTF-8
|
|
208
|
+
def self.load_file(path, opts = nil)
|
|
209
|
+
load_file_native(path, opts)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Generates +obj+ as JSON and writes it to +path+, streaming the
|
|
213
|
+
# generator's buffer straight to disk—no intermediate Ruby String.
|
|
214
|
+
#
|
|
215
|
+
# @example
|
|
216
|
+
# NOSJ.write_file("out.json", {"a" => [1, true]}) #=> 14
|
|
217
|
+
# NOSJ.write_file("pretty.json", obj, indent: " ", object_nl: "\n")
|
|
218
|
+
#
|
|
219
|
+
# @param path [String] the file to (over)write
|
|
220
|
+
# @param obj [Object] the value tree to generate
|
|
221
|
+
# @param opts [Hash, nil] the same options as {.generate}
|
|
222
|
+
# @return [Integer] the number of bytes written, like File.write
|
|
223
|
+
# @raise [SystemCallError] +Errno::ENOENT+ and friends, like File.write
|
|
224
|
+
# @raise [GeneratorError] like {.generate}
|
|
225
|
+
def self.write_file(path, obj, opts = nil)
|
|
226
|
+
write_file_native(path, obj, opts)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Wraps a JSON file as a lazy document ({.lazy} for files): the file
|
|
230
|
+
# is memory-mapped read-only, so beyond one sequential UTF-8 check,
|
|
231
|
+
# pages you never read are never loaded from disk. The mapping lives
|
|
232
|
+
# as long as any node on it; the file must not be modified while it
|
|
233
|
+
# is in use.
|
|
234
|
+
#
|
|
235
|
+
# @example
|
|
236
|
+
# doc = NOSJ.load_lazy_file("huge.json")
|
|
237
|
+
# doc["users"][3]["name"] # touches only these pages
|
|
238
|
+
#
|
|
239
|
+
# @param path [String] the file to wrap (UTF-8)
|
|
240
|
+
# @param opts [Hash, nil] {.parse} options applied on materialization
|
|
241
|
+
# @return [NOSJ::Lazy, Object]
|
|
242
|
+
# @raise [SystemCallError] +Errno::ENOENT+ and friends
|
|
243
|
+
# @raise [RuntimeError] when the file is not UTF-8 or the root is malformed
|
|
244
|
+
def self.load_lazy_file(path, opts = nil)
|
|
245
|
+
load_lazy_file_native(path, opts)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# {.at_pointer} against a file: memory-maps it, resolves the pointer,
|
|
249
|
+
# materializes only the matched subtree, and never reads the rest
|
|
250
|
+
# into Ruby.
|
|
251
|
+
#
|
|
252
|
+
# @example
|
|
253
|
+
# NOSJ.at_pointer_file("huge.json", "/users/3/name")
|
|
254
|
+
#
|
|
255
|
+
# @param path [String] the file to query (UTF-8)
|
|
256
|
+
# @param pointer [String] an RFC 6901 JSON Pointer
|
|
257
|
+
# @param opts [Hash, nil] materialization options
|
|
258
|
+
# @return [Object, nil] nil when the pointer misses
|
|
259
|
+
# @raise [ArgumentError] for malformed pointers
|
|
260
|
+
def self.at_pointer_file(path, pointer, opts = nil)
|
|
261
|
+
at_pointer_file_native(path, pointer, opts)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# {.dig} against a file: the Hash#dig-shaped counterpart of
|
|
265
|
+
# {.at_pointer_file}. Negative indices resolve to nil.
|
|
266
|
+
#
|
|
267
|
+
# @example
|
|
268
|
+
# NOSJ.dig_file("huge.json", "users", 3, "name")
|
|
269
|
+
#
|
|
270
|
+
# @param path [String] the file to query (UTF-8)
|
|
271
|
+
# @param path_elements [Array<String, Symbol, Integer>]
|
|
272
|
+
# @return [Object, nil]
|
|
273
|
+
def self.dig_file(path, *path_elements)
|
|
274
|
+
dig_file_native(path, path_elements)
|
|
275
|
+
end
|
|
193
276
|
end
|
data/sig/nosj.rbs
CHANGED
|
@@ -47,6 +47,60 @@ module NOSJ
|
|
|
47
47
|
|
|
48
48
|
def self.at_pointers: (String source, Array[String] pointers, ?opts opts) -> Array[value]
|
|
49
49
|
|
|
50
|
+
def self.lazy: (String source, ?opts opts) -> (Lazy | value)
|
|
51
|
+
|
|
52
|
+
def self.load_file: (String path, ?opts opts) -> value
|
|
53
|
+
|
|
54
|
+
def self.write_file: (String path, untyped obj, ?opts opts) -> Integer
|
|
55
|
+
|
|
56
|
+
def self.load_lazy_file: (String path, ?opts opts) -> (Lazy | value)
|
|
57
|
+
|
|
58
|
+
def self.at_pointer_file: (String path, String pointer, ?opts opts) -> value
|
|
59
|
+
|
|
60
|
+
def self.dig_file: (String path, *path_element path_elements) -> value
|
|
61
|
+
|
|
62
|
+
# A lazy view of one JSON container: children resolve on first
|
|
63
|
+
# access (containers as further Lazy nodes, scalars as values,
|
|
64
|
+
# misses as nil) and are cached on the node.
|
|
65
|
+
class Lazy
|
|
66
|
+
include Enumerable[untyped]
|
|
67
|
+
|
|
68
|
+
def []: (path_element token) -> (Lazy | value)
|
|
69
|
+
|
|
70
|
+
def dig: (path_element first, *path_element rest) -> (Lazy | value)
|
|
71
|
+
|
|
72
|
+
def at_pointer: (String pointer) -> (Lazy | value)
|
|
73
|
+
|
|
74
|
+
def value: () -> value
|
|
75
|
+
|
|
76
|
+
alias materialize value
|
|
77
|
+
|
|
78
|
+
def to_h: () -> Hash[String | Symbol, value]
|
|
79
|
+
|
|
80
|
+
def to_a: () -> Array[value]
|
|
81
|
+
|
|
82
|
+
def object?: () -> bool
|
|
83
|
+
|
|
84
|
+
def array?: () -> bool
|
|
85
|
+
|
|
86
|
+
def keys: () -> Array[String]
|
|
87
|
+
|
|
88
|
+
def size: () -> Integer
|
|
89
|
+
|
|
90
|
+
alias length size
|
|
91
|
+
|
|
92
|
+
alias count size
|
|
93
|
+
|
|
94
|
+
def empty?: () -> bool
|
|
95
|
+
|
|
96
|
+
def each: () { (untyped) -> void } -> self
|
|
97
|
+
| () -> Enumerator[untyped, self]
|
|
98
|
+
|
|
99
|
+
def inspect: () -> String
|
|
100
|
+
|
|
101
|
+
alias to_s inspect
|
|
102
|
+
end
|
|
103
|
+
|
|
50
104
|
# Defined by `require "nosj/multi_json"`. The runtime superclass is
|
|
51
105
|
# multi_json's Adapter (whose namespace differs across multi_json
|
|
52
106
|
# versions), so it is not declared here.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nosj
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yaroslav Markin
|
|
@@ -25,7 +25,9 @@ dependencies:
|
|
|
25
25
|
version: 0.9.91
|
|
26
26
|
description: 'gem nosj is an extremely fast, json-gem-compatible JSON parser and generator
|
|
27
27
|
for Ruby: Rust and SIMD via the first-party nosj crate, precompiled platform gems
|
|
28
|
-
with per-platform PGO, partial parsing (JSON Pointer, single and batch),
|
|
28
|
+
with per-platform PGO, partial parsing (JSON Pointer, single and batch), lazy documents
|
|
29
|
+
that parse a value only when you touch it, file APIs that parse, generate, and query
|
|
30
|
+
files natively (memory-mapped, so unread pages never leave the disk), allocation-free
|
|
29
31
|
validation, and a one-line JSON module drop-in.'
|
|
30
32
|
email:
|
|
31
33
|
- yaroslav@markin.net
|
|
@@ -42,6 +44,7 @@ files:
|
|
|
42
44
|
- README.md
|
|
43
45
|
- ext/nosj/Cargo.toml
|
|
44
46
|
- ext/nosj/extconf.rb
|
|
47
|
+
- ext/nosj/src/files.rs
|
|
45
48
|
- ext/nosj/src/gen/errors.rs
|
|
46
49
|
- ext/nosj/src/gen/hash_iter.rs
|
|
47
50
|
- ext/nosj/src/gen/keys.rs
|
|
@@ -49,6 +52,7 @@ files:
|
|
|
49
52
|
- ext/nosj/src/gen/opts.rs
|
|
50
53
|
- ext/nosj/src/gen/ruby.rs
|
|
51
54
|
- ext/nosj/src/gen/walker.rs
|
|
55
|
+
- ext/nosj/src/lazy.rs
|
|
52
56
|
- ext/nosj/src/lib.rs
|
|
53
57
|
- ext/nosj/src/parse.rs
|
|
54
58
|
- ext/nosj/src/pointer.rs
|
|
@@ -56,6 +60,7 @@ files:
|
|
|
56
60
|
- ext/nosj/src/state.rs
|
|
57
61
|
- lib/nosj.rb
|
|
58
62
|
- lib/nosj/json.rb
|
|
63
|
+
- lib/nosj/lazy.rb
|
|
59
64
|
- lib/nosj/multi_json.rb
|
|
60
65
|
- lib/nosj/native.rb
|
|
61
66
|
- lib/nosj/version.rb
|