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.
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?}) and partial parsing
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.1.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), allocation-free
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