leptris 1.9.17-aarch64-linux → 1.9.20-aarch64-linux

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb75550db96ee7caa7354e9e6c15560218a24aaa78876b6a394fb2749df32926
4
- data.tar.gz: 4d944de7936ec570782375e1605ea5d452425fae6bfefeca5588c6aced95248b
3
+ metadata.gz: f771cd132406876908f1fd005de0fb47dc6c39ab1c92e7af8bd0bf44e813262b
4
+ data.tar.gz: cffb7a6bf701d9d9dbbf3eaca31e29fda0f4645ad9f3579ccb95df8873936649
5
5
  SHA512:
6
- metadata.gz: 813314f3f5fb49ef7871b3bbd95206f7080757c686fc0ffd252444719ee9427252c579d2a2293a6d2a2eeb4c820b731234ea936b483562f9e58508d6d9eaa96d
7
- data.tar.gz: 29f50102bf31307bd6afa07d3856f3a00b72d8b5d7264143d24ba436c8b8683387846932f1ca3d49905fac003713c76952bad50d45ad9eb3a58aa26376fea12c
6
+ metadata.gz: b8ce9c02cf8c7bbdf86ba36f061a17847060b7f82064e8c95a354b2310b8ebe8fff1deaf13ddcc9351d201a5059a3c281483371e96b10727502abb3a12f10bbc
7
+ data.tar.gz: 3fee7ac669e47714269df9821d32b99b3d9ca96d91b03caf4457b4f73ed8fef4180b4daf598a7fbc870fdd9cfde8208c42560ced83b1a59d2341ccfecef070f9
data/CHANGELOG.md CHANGED
@@ -5,6 +5,66 @@ All notable changes to Leptris will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.9.20] - 2026-08-27
9
+
10
+ ### Changed
11
+
12
+ - **Pull event loop allocation trim**: `Parser#next_event` reads the
13
+ event's type/name/text through layout-derived offsets
14
+ (`PullEventStruct.offset_of` — the struct stays the ABI's single
15
+ source of truth) instead of allocating a struct wrapper per event;
16
+ `capture_attrs` builds its hash without the enumerator machinery
17
+ and answers nil for zero attributes. Streaming a 300-element
18
+ document: 2.9 ms -> 2.3 ms per parse (**-20%**). The remaining
19
+ per-event cost is FFI dispatch — the batch ask is filed upstream
20
+ (leptris/leptris#589).
21
+
22
+ ### Meta
23
+
24
+ - Streaming-path benchmarks added to the round's report: pull with
25
+ events measured 145x slower than DOM-parsing the same bytes
26
+ (dispatch-dominated); iterparse's 60% system-time signature
27
+ documented on leptris/leptris#563 (C-side release churn).
28
+
29
+ ## [1.9.19] - 2026-08-27
30
+
31
+ Lockstep with libleptris 1.9.3 — unblocks the 1.9.18 release
32
+ (v1.9.2 did not compile on musl, upstream #582).
33
+
34
+ ### Added
35
+
36
+ - **`Document#comments`**: document-level comments — parsed
37
+ `<!-- ... -->` outside the root, prolog then epilog, in document
38
+ order (upstream #578). UTF-8, version-memoized like the PI list;
39
+ the companion reader to `#processing_instructions`.
40
+
41
+ ### Fixed
42
+
43
+ - Rides the engine's v1.9.3 fixes: dataless PIs parse correctly
44
+ (#577 — `<?pi?>` no longer clobbers its closing `?`), epilog
45
+ comments are kept and serialize after the root (#578), and
46
+ attribute values normalize per XML 1.0 §3.3.3 (#576 — whitespace
47
+ collapsing and entity decoding in attribute values).
48
+
49
+ ## [1.9.18] - 2026-08-27
50
+
51
+ Lockstep with libleptris 1.9.2 (covers 1.9.1 — the XSLT engine — and
52
+ 1.9.2, the options-struct ABI freeze; the binding's three-field
53
+ SerializeOptions layout is the frozen shape, so no binding change was
54
+ required for the fix).
55
+
56
+ ### Added
57
+
58
+ - **`Leptris::XML::XSLT` — XSLT 1.0 transforms**: the engine compiles
59
+ a stylesheet ONCE into an immutable instruction forest, then applies
60
+ it to any number of documents. `XSLT.parse(xml)` /
61
+ `XSLT.parse_file(path)` (resolves §2.7 embedded stylesheets);
62
+ `Stylesheet#apply_to(doc)` returns the result as an owning,
63
+ queryable Document; `Stylesheet#serialize(doc)` keeps top-level
64
+ text nodes and result fragments. Bad select expressions are
65
+ rejected at COMPILE time. The custom XPath-function handler seam
66
+ attaches mirror-only until a use case arrives.
67
+
8
68
  ## [1.9.17] - 2026-08-27
9
69
 
10
70
  ### Changed
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
8
8
  # Pin for `rake compile` and the platform-gem builds. Keep in lockstep
9
9
  # with .github/workflows/build.yml (which calls `rake compile`) and the
10
10
  # CHANGELOG when libleptris releases.
11
- LIBLEPTRIS_VERSION = "1.9.0"
11
+ LIBLEPTRIS_VERSION = "1.9.3"
12
12
 
13
13
  CMAKE_FLAGS = %w[
14
14
  -DCMAKE_BUILD_TYPE=Release
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.17"
4
+ VERSION = "1.9.20"
5
5
  end
@@ -311,6 +311,21 @@ class Leptris::XML::Document
311
311
  @freed.state == :freed || @c_ptr.nil?
312
312
  end
313
313
 
314
+ # Document-level comments — parsed <!-- ... --> outside the
315
+ # root element, prolog then epilog, in document order (the
316
+ # companion reader to #processing_instructions; libleptris
317
+ # 1.9.3, upstream #578). Version-memoized like the PI list.
318
+ def comments
319
+ return @comments if @comments_version == @version
320
+ count = Leptris::XML::FFI.leptris_document_comment_count(@c_ptr)
321
+ result = Array.new(count) do |i|
322
+ Leptris::XML::FFI.leptris_document_comment_content(@c_ptr, i)
323
+ end
324
+ @comments = result
325
+ @comments_version = @version
326
+ result
327
+ end
328
+
314
329
  # The thread-global last-failure [line, column] (1-based), or nil
315
330
  # when no error is recorded — the position companion to
316
331
  # Document#last_error; populated by recover parses.
@@ -42,6 +42,7 @@ module Leptris
42
42
  typedef :pointer, :leptris_xpath_result
43
43
  typedef :pointer, :leptris_xpath_ns_set
44
44
  typedef :pointer, :leptris_xpath_compiled
45
+ typedef :pointer, :leptris_xslt
45
46
  typedef :pointer, :leptris_pull_parser
46
47
  typedef :pointer, :leptris_iterparse
47
48
  typedef :pointer, :leptris_xpath_var_set
@@ -124,6 +125,13 @@ module Leptris
124
125
  [:leptris_document, :size_t], :string
125
126
  attach_function :leptris_document_add_pi,
126
127
  [:leptris_document, :string, :string], :pointer
128
+ # Document-level comments (libleptris 1.9.3, #578): parsed
129
+ # <!-- ... --> outside the root, prolog then epilog, in
130
+ # document order. Read-only surface (no add_comment yet).
131
+ attach_function :leptris_document_comment_count,
132
+ [:leptris_document], :size_t
133
+ attach_function :leptris_document_comment_content,
134
+ [:leptris_document, :size_t], :string
127
135
  # Options-struct parse (v1.6.0): supersedes the flags variants;
128
136
  # the flags path remains bound for compatibility.
129
137
  attach_function :leptris_parse_string_ex,
@@ -478,7 +486,11 @@ module Leptris
478
486
  attach_function :leptris_xpath_compiled_eval_vars,
479
487
  [:leptris_xpath_compiled, :leptris_document, :leptris_element,
480
488
  :leptris_xpath_var_set], :leptris_xpath_result
489
+ # Source text of a compiled expression (owned by the handle).
490
+ attach_function :leptris_xpath_compiled_text,
491
+ [:leptris_xpath_compiled], :string
481
492
  attach_function :leptris_xpath_compiled_free,
493
+
482
494
  [:leptris_xpath_compiled], :void
483
495
 
484
496
  attach_function :leptris_sax_parse,
@@ -539,7 +551,23 @@ module Leptris
539
551
  [:leptris_element, :pointer, :size_t, :pointer, :pointer], :size_t
540
552
  attach_function :leptris_document_save_file,
541
553
  [:leptris_document, :string, :pointer], :leptris_status
554
+ # XSLT 1.0 engine (libleptris 1.9.1): compile once, apply many.
555
+ attach_function :leptris_xslt_parse,
556
+ [:string, :size_t], :leptris_xslt
557
+ attach_function :leptris_xslt_parse_file,
558
+ [:string], :leptris_xslt
559
+ attach_function :leptris_xslt_free,
560
+ [:leptris_xslt], :void
561
+ # Result tree is a document (free with document_free); errors
562
+ # surface via document_last_error on NULL.
563
+ attach_function :leptris_xslt_apply,
564
+ [:leptris_xslt, :leptris_document], :leptris_document
565
+ # apply + serialize in one call, keeping top-level text nodes
566
+ # and result fragments the tree API would flatten.
567
+ attach_function :leptris_xslt_apply_string,
568
+ [:leptris_xslt, :leptris_document], :pointer
542
569
  attach_function :leptris_c14n_canonicalize,
570
+
543
571
  [:leptris_document, :int, :int], :pointer
544
572
  attach_function :leptris_c14n_canonicalize_subtree,
545
573
  [:leptris_element, :int, :int], :pointer
@@ -648,6 +676,7 @@ module Leptris
648
676
  leptris_status_string leptris_last_error
649
677
  leptris_document_last_error leptris_document_encoding
650
678
  leptris_document_pi_target leptris_document_pi_data
679
+ leptris_document_comment_content
651
680
  leptris_element_name leptris_element_text leptris_element_prefix
652
681
  leptris_element_namespace
653
682
  leptris_element_attribute leptris_element_attribute_ns
@@ -60,6 +60,14 @@ module Leptris::XML::Pull
60
60
  self
61
61
  end
62
62
 
63
+ # Hot-loop offsets: derived from PullEventStruct's layout (the
64
+ # struct stays the single source of truth for the ABI), read via
65
+ # get_int/get_pointer so the per-event struct-wrapper allocation
66
+ # disappears from streaming loops.
67
+ NAME_OFFSET = Leptris::XML::FFI::PullEventStruct.offset_of(:name)
68
+ TEXT_OFFSET = Leptris::XML::FFI::PullEventStruct.offset_of(:text)
69
+ private_constant :NAME_OFFSET, :TEXT_OFFSET
70
+
63
71
  # Advances the cursor and returns the next Event, or nil after the
64
72
  # end of the document. Attribute values are captured during
65
73
  # start_element because the C accessors are valid only for the
@@ -67,26 +75,31 @@ module Leptris::XML::Pull
67
75
  def next_event
68
76
  raw = Leptris::XML::FFI.leptris_pull_next(@handle)
69
77
  return nil if raw.null?
70
- event = Leptris::XML::FFI::PullEventStruct.new(raw)
71
- type = TYPES[event[:type]]
72
- name = read_owned(event[:name])
73
- text = read_owned(event[:text])
78
+ type = TYPES[raw.get_int(0)]
79
+ name_ptr = raw.get_pointer(NAME_OFFSET)
80
+ text_ptr = raw.get_pointer(TEXT_OFFSET)
74
81
  attrs = type == :start_element ? capture_attrs : nil
75
- Event.new(type: type, name: name, text: text, attrs: attrs)
82
+ Event.new(
83
+ type: type,
84
+ name: name_ptr.null? ? nil : name_ptr.read_string.force_encoding(Encoding::UTF_8),
85
+ text: text_ptr.null? ? nil : text_ptr.read_string.force_encoding(Encoding::UTF_8),
86
+ attrs: attrs
87
+ )
76
88
  end
77
89
 
78
90
  private
79
91
 
80
- def read_owned(ptr)
81
- ptr.null? ? nil : ptr.read_string.force_encoding(Encoding::UTF_8)
82
- end
83
-
84
92
  def capture_attrs
85
93
  count = Leptris::XML::FFI.leptris_pull_attr_count(@handle)
86
- count.times.each_with_object({}) do |i, hash|
94
+ return nil if count.zero?
95
+ hash = {}
96
+ i = 0
97
+ while i < count
87
98
  hash[Leptris::XML::FFI.leptris_pull_attr_name(@handle, i)] =
88
99
  Leptris::XML::FFI.leptris_pull_attr_value(@handle, i)
100
+ i += 1
89
101
  end
102
+ hash
90
103
  end
91
104
  end
92
105
 
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+
5
+ module Leptris::XML::XSLT
6
+ module_function
7
+
8
+ def parse(stylesheet_xml)
9
+ Stylesheet.parse(stylesheet_xml)
10
+ end
11
+
12
+ def parse_file(path)
13
+ Stylesheet.parse_file(path)
14
+ end
15
+
16
+ # A compiled XSLT 1.0 stylesheet (libleptris 1.9.1): the stylesheet
17
+ # parses ONCE into an immutable instruction forest — patterns to
18
+ # XPath ASTs, selects through the compiled-XPath path — then applies
19
+ # to any number of documents with no re-parsing.
20
+ #
21
+ # style = Leptris::XML::XSLT.parse(<<~XSL)
22
+ # <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
23
+ # <xsl:template match="/"><out><xsl:value-of select="count(//item)"/></out></xsl:template>
24
+ # </xsl:stylesheet>
25
+ # XSL
26
+ # style.apply_to(doc) # => result Document (queryable tree)
27
+ # style.serialize(doc) # => String (fragments and top-level
28
+ # # text nodes preserved)
29
+ #
30
+ class Stylesheet
31
+ # GC-managed compiled handle.
32
+ class Handle < ::FFI::AutoPointer
33
+ def self.release(ptr)
34
+ Leptris::XML::FFI.leptris_xslt_free(ptr)
35
+ end
36
+ end
37
+
38
+ # Compile a stylesheet. Raises XPathError on a stylesheet syntax
39
+ # error (detail on the thread-global last error).
40
+ def self.parse(stylesheet_xml)
41
+ raw = Leptris::XML::FFI.leptris_xslt_parse(
42
+ stylesheet_xml.to_s, stylesheet_xml.to_s.bytesize)
43
+ if raw.null?
44
+ raise Leptris::XML::XPathError,
45
+ "stylesheet parse failed: #{Leptris::XML::FFI.leptris_last_error}"
46
+ end
47
+ new(Handle.new(raw))
48
+ end
49
+
50
+ # Compile from a file. Resolves §2.7 embedded stylesheets
51
+ # (xml-stylesheet PI with an href="#id" fragment) when the file's
52
+ # root is not itself a stylesheet.
53
+ def self.parse_file(path)
54
+ raw = Leptris::XML::FFI.leptris_xslt_parse_file(path.to_s)
55
+ if raw.null?
56
+ raise Leptris::XML::XPathError,
57
+ "stylesheet parse failed: #{Leptris::XML::FFI.leptris_last_error}"
58
+ end
59
+ new(Handle.new(raw))
60
+ end
61
+
62
+ def initialize(handle)
63
+ @handle = handle
64
+ end
65
+
66
+ # Apply to +document+ (not modified) and return the result tree
67
+ # as an owning Document — query it with xpath/css like any other.
68
+ def apply_to(document)
69
+ raw = Leptris::XML::FFI.leptris_xslt_apply(@handle, document.c_ptr)
70
+ if raw.null?
71
+ raise Leptris::XML::XPathError,
72
+ "transform failed: #{document.last_error || Leptris::XML::FFI.leptris_last_error}"
73
+ end
74
+ Leptris::XML::Document.wrap(raw)
75
+ end
76
+
77
+ # Apply and serialize in one call — keeps top-level text nodes and
78
+ # result fragments that the tree API would flatten.
79
+ def serialize(document)
80
+ str_ptr = Leptris::XML::FFI.leptris_xslt_apply_string(
81
+ @handle, document.c_ptr)
82
+ Leptris::XML::FFI.read_owned_string(str_ptr)
83
+ end
84
+ end
85
+ end
data/lib/leptris/xml.rb CHANGED
@@ -22,6 +22,7 @@ module Leptris
22
22
  autoload :Serialization, "leptris/xml/serialization"
23
23
  autoload :SAX, "leptris/xml/sax"
24
24
  autoload :XPath, "leptris/xml/xpath"
25
+ autoload :XSLT, "leptris/xml/xslt"
25
26
  autoload :Pull, "leptris/xml/pull"
26
27
  autoload :Iterparse, "leptris/xml/iterparse"
27
28
 
data/lib/libleptris.so CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.17
4
+ version: 1.9.20
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.
@@ -103,6 +103,7 @@ files:
103
103
  - lib/leptris/xml/serialization.rb
104
104
  - lib/leptris/xml/text.rb
105
105
  - lib/leptris/xml/xpath.rb
106
+ - lib/leptris/xml/xslt.rb
106
107
  - lib/libleptris.so
107
108
  homepage: https://github.com/leptris/leptris-ruby
108
109
  licenses: