leptris 1.9.29 → 1.9.31

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: f91b02217a813c5c7c8eb267b6b82b3eb7689d857fbd76b7a8b108962d56d18c
4
- data.tar.gz: c15ad7755d849144c45176d4e28f819ac090927c8a1e1d6ad58c89fe8a3006fe
3
+ metadata.gz: 61af1b12021e7a1cf22f04004ea36d27df6f740ff135d039854c51497abf87f3
4
+ data.tar.gz: 0a842331a02a988b8bfce2920f8878b87c5ff5fdbd4c471f2f04694f4a1b3465
5
5
  SHA512:
6
- metadata.gz: db0800af7aafe8cf8efb263a0fde99d8876b3d8f487a9cbe738bf2d086a9be8a9e6805e9033b3b695b9064e58d4593f0aee4361dcdc55fa2f920e5a71484acb7
7
- data.tar.gz: ae1282d974199e33a98c9361aee2afb36b7dacfb782259380688cd13e480065d3ff59108be44f868b3283f0f11fb958c37d16f28c9eb5fcfc1a6ac6331020c26
6
+ metadata.gz: e832da72a4e2fe0ddede057023c1e981705a41f196d85e7d7e5d61051d524bddfc2305065fef940ef60af48b87e978bc46be7e61030d598de83fb916db8bba3e
7
+ data.tar.gz: 7a266c15d5983229591bf414ad0dde95cc93018ba01388bf721f98977c026c164797e1dc805fc9a46625a6b2037c7709a65cb0e0640ca5ed6c46f40614dd37c0
data/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ 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.31] - 2026-08-28
9
+
10
+ ### Fixed
11
+
12
+ - **PI data consumes the leading whitespace run (leptris-ruby#85,
13
+ libxml2 parity)**: the engine retains the full whitespace run
14
+ after the PI target as data where libxml2 consumes it at parse
15
+ time — every Ruby-facing PI-data read now normalizes through one
16
+ seam helper (`FFI.read_pi_data`): `PI#content`,
17
+ `Document#processing_instructions` pairs, the SAX
18
+ `processing_instruction` callback, pull `:pi` events, and
19
+ recorder `:pi` events. Whitespace-only data reads as `""`, so
20
+ "pi-without-data" PIs report empty consistently; trailing
21
+ whitespace is kept (libxml2 trims only the leading run).
22
+ Read-time only — `PI#data=` still stores verbatim.
23
+
24
+ ## [1.9.30] - 2026-08-28
25
+
26
+ ### Fixed
27
+
28
+ - **DTD ATTLIST defaults no longer injected by default** (vendored
29
+ libleptris 1.9.8, leptris/leptris#606): raw parses leave ATTLIST
30
+ default attributes out — the libxml2/Nokogiri default the ecosystem
31
+ compares against, and what W3C C14N 1.1 example 3.3's canonical
32
+ form assumes. New `Leptris::XML::ParseOptions::DTDATTR` /
33
+ `.dtdattr` / `#dtdattr?` / `#dtdattr=` opts in (libxml2
34
+ XML_PARSE_DTDATTR parity).
35
+
8
36
  ## [1.9.29] - 2026-08-28
9
37
 
10
38
  ### Fixed
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.7"
11
+ LIBLEPTRIS_VERSION = "1.9.8"
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.29"
4
+ VERSION = "1.9.31"
5
5
  end
@@ -295,7 +295,8 @@ class Leptris::XML::Document
295
295
  count = Leptris::XML::FFI.leptris_document_pi_count(@c_ptr)
296
296
  result = count.times.map do |i|
297
297
  [Leptris::XML::FFI.leptris_document_pi_target(@c_ptr, i),
298
- Leptris::XML::FFI.leptris_document_pi_data(@c_ptr, i)]
298
+ Leptris::XML::FFI.read_pi_data(
299
+ Leptris::XML::FFI.leptris_document_pi_data(@c_ptr, i)).to_s]
299
300
  end
300
301
  @processing_instructions = result
301
302
  @pi_version = @version
@@ -734,6 +734,10 @@ module Leptris
734
734
 
735
735
  LEPTRIS_PARSE_DEFAULT = 0
736
736
  LEPTRIS_PARSE_DROP_WS_TEXT = 1
737
+ # libleptris >= 1.9.8 (#606): apply DTD ATTLIST default (and
738
+ # #FIXED) attribute values at parse time — libxml2
739
+ # XML_PARSE_DTDATTR opt-in parity.
740
+ LEPTRIS_PARSE_DTDATTR = 2
737
741
 
738
742
  # The headers' contract is UTF-8 for every C string, but FFI's
739
743
  # read_string hands back ASCII-8BIT — the platform default
@@ -920,6 +924,16 @@ module Leptris
920
924
  [raw, status.read_int]
921
925
  end
922
926
 
927
+ # libxml2 consumes the whole whitespace run following the PI
928
+ # target when parsing; the engine retains it as data
929
+ # (leptris-ruby#85). Every Ruby-facing PI-data read normalizes
930
+ # here — whitespace-only data becomes "" so
931
+ # "pi-without-data" PIs report empty consistently. Read-time
932
+ # only: PI#data= stores verbatim.
933
+ def self.read_pi_data(data)
934
+ data&.sub(/\A[ \t\r\n]+/, "")
935
+ end
936
+
923
937
  # Thread-local scratch buffers for batch handle fetches (round
924
938
  # XX): the per-call MemoryPointer new/free was pure cold-path
925
939
  # overhead — two allocations and a GC visit per children() /
@@ -9,6 +9,14 @@ class Leptris::XML::ParseOptions
9
9
  # way do not round-trip pretty-printed formatting byte-for-byte.
10
10
  NOBLANKS = Leptris::XML::FFI::LEPTRIS_PARSE_DROP_WS_TEXT
11
11
 
12
+ # Apply DTD ATTLIST default (and #FIXED) attribute values at parse
13
+ # time (libleptris >= 1.9.8, leptris/leptris#606). Matches libxml2's
14
+ # XML_PARSE_DTDATTR and Nokogiri's dtdload+dtdattr semantics. Off by
15
+ # default: the ecosystem compares against libxml2's no-DTDATTR
16
+ # default, and W3C C14N 1.1 example 3.3's canonical form excludes
17
+ # defaulted attributes.
18
+ DTDATTR = Leptris::XML::FFI::LEPTRIS_PARSE_DTDATTR
19
+
12
20
  attr_reader :flags
13
21
 
14
22
  # Recover (libleptris 1.9.0, #547): a parse failure returns an
@@ -36,6 +44,18 @@ class Leptris::XML::ParseOptions
36
44
  @flags & NOBLANKS != 0
37
45
  end
38
46
 
47
+ def self.dtdattr
48
+ new(DTDATTR)
49
+ end
50
+
51
+ def dtdattr?
52
+ @flags & DTDATTR != 0
53
+ end
54
+
55
+ def dtdattr=(value)
56
+ if value then @flags |= DTDATTR else @flags &= ~DTDATTR end
57
+ end
58
+
39
59
  def recover?
40
60
  @recover == true
41
61
  end
@@ -16,7 +16,8 @@ class Leptris::XML::ProcessingInstruction < Leptris::XML::Node
16
16
  def content
17
17
  return @content if memo_hit?(@content_version)
18
18
  ensure_alive!
19
- result = Leptris::XML::FFI.leptris_pi_node_get_data(@c_ptr).to_s
19
+ result = Leptris::XML::FFI.read_pi_data(
20
+ Leptris::XML::FFI.leptris_pi_node_get_data(@c_ptr)).to_s
20
21
  if @document
21
22
  @content = result
22
23
  @content_version = @document.version
@@ -84,13 +84,14 @@ module Leptris::XML::Pull
84
84
  base = i * stride
85
85
  name_ptr = buffer.get_pointer(base + name_off)
86
86
  text_ptr = buffer.get_pointer(base + text_off)
87
- Event.new(
88
- TYPES[buffer.get_int(base + type_off)],
87
+ type = TYPES[buffer.get_int(base + type_off)]
88
+ text = text_ptr.null? ? nil :
89
+ text_ptr.read_string.force_encoding(Encoding::UTF_8)
90
+ text = Leptris::XML::FFI.read_pi_data(text) if type == :pi
91
+ Event.new(type,
89
92
  name_ptr.null? ? nil :
90
93
  name_ptr.read_string.force_encoding(Encoding::UTF_8),
91
- text_ptr.null? ? nil :
92
- text_ptr.read_string.force_encoding(Encoding::UTF_8),
93
- nil
94
+ text, nil
94
95
  )
95
96
  end
96
97
  # The attr mirror holds the batch's most recent start.
@@ -125,11 +126,13 @@ module Leptris::XML::Pull
125
126
  name_ptr = raw.get_pointer(NAME_OFFSET)
126
127
  text_ptr = raw.get_pointer(TEXT_OFFSET)
127
128
  attrs = type == :start_element ? capture_attrs : nil
129
+ text = text_ptr.null? ? nil :
130
+ text_ptr.read_string.force_encoding(Encoding::UTF_8)
131
+ text = Leptris::XML::FFI.read_pi_data(text) if type == :pi
128
132
  Event.new(
129
133
  type,
130
134
  name_ptr.null? ? nil : name_ptr.read_string.force_encoding(Encoding::UTF_8),
131
- text_ptr.null? ? nil : text_ptr.read_string.force_encoding(Encoding::UTF_8),
132
- attrs
135
+ text, attrs
133
136
  )
134
137
  end
135
138
 
@@ -149,7 +149,8 @@ class Leptris::XML::SAX::Parser
149
149
 
150
150
  if overridden?(:processing_instruction)
151
151
  s[:processing_instruction] = callback(:void, [:pointer, :string, :string]) do |_, target, data|
152
- handler.processing_instruction(utf8(target), utf8(data))
152
+ handler.processing_instruction(
153
+ utf8(target), Leptris::XML::FFI.read_pi_data(utf8(data)))
153
154
  end
154
155
  end
155
156
 
@@ -122,9 +122,11 @@ class Leptris::XML::SAX::Recorder
122
122
  kind = KIND_BY_CODE[fields[i * 9]]
123
123
  if kind && (wanted.nil? || wanted[kind])
124
124
  base = i * 9 + 1
125
+ text = slice(arena, fields[base + 2], fields[base + 3])
126
+ text = Leptris::XML::FFI.read_pi_data(text) if kind == :pi
125
127
  yield kind,
126
128
  slice(arena, fields[base], fields[base + 1]),
127
- slice(arena, fields[base + 2], fields[base + 3]),
129
+ text,
128
130
  attrs_from(arena, fields[base + 4], fields[base + 5]),
129
131
  fields[base + 6], fields[base + 7]
130
132
  end
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.29
4
+ version: 1.9.31
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.