Module: Moxml::Adapter::Leptris::Materialize

Included in:
Moxml::Adapter::Leptris
Defined in:
lib/moxml/adapter/leptris/materialize.rb

Constant Summary collapse

EMPTY_FIELDS =
Materializer::EMPTY_ATTRIBUTES

Instance Method Summary collapse

Instance Method Details

#bulk_materialize?Boolean

Bulk materialization (issues #132/#143): a Ruby-side post-order recursion over RAW C pointers — no binding wrapper per node, no per-node FFI callback, no depth memo (depth is a recursion parameter), no record allocation. Child lists arrive through the batch leptris_node_children call; element fields are read straight off the C handles (the binding's own first/next attribute iteration face), skipping the per-attribute wrapper its public API allocates. The unprefixed-attribute namespace read is skipped because the answer is nil by definition (no-namespace attributes).

Returns:

  • (Boolean)


37
38
39
# File 'lib/moxml/adapter/leptris/materialize.rb', line 37

def bulk_materialize?
  true
end

#emit_element_fields(ptr, buffers, depth) {|:element, name, prefix, uri, ns_buf, attrs_buf, nil, depth| ... } ⇒ Object

Yields:

  • (:element, name, prefix, uri, ns_buf, attrs_buf, nil, depth)


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/moxml/adapter/leptris/materialize.rb', line 94

def emit_element_fields(ptr, buffers, depth, &block)
  f = BINDING_FFI
  ns_buf = buffers.namespaces
  attrs_buf = buffers.attributes

  name = f.leptris_element_name(ptr)
  prefix = f.leptris_element_prefix(ptr)
  uri = f.leptris_element_namespace(ptr)
  uri = nil if uri && uri.empty?

  # Own declarations only — same shape as the generic path's
  # Element#declared_namespaces (issue #138).
  ns_buf.clear
  i = 0
  ns_count = f.leptris_element_namespace_count(ptr)
  while i < ns_count
    ns_buf << f.leptris_element_namespace_decl_prefix(ptr, i)
    ns_buf << f.leptris_element_namespace_decl_uri(ptr, i)
    i += 1
  end

  attrs_buf.clear
  attr = f.leptris_element_first_attribute(ptr)
  until attr.nil? || attr.null?
    attr_name = f.leptris_attribute_get_name(attr)
    attr_value = f.leptris_attribute_get_value(ptr, attr)
    colon = attr_name.index(":")
    if colon
      attr_prefix = attr_name[0, colon]
      # Local name + separate prefix, matching the generic
      # path's resolver semantics (moxml's canonical shape).
      attr_name = attr_name[(colon + 1)..]
      attr_uri = f.leptris_attribute_namespace_uri(attr)
    else
      attr_prefix = nil
      attr_uri = nil
    end
    attrs_buf << attr_name << attr_value << attr_uri << attr_prefix
    attr = f.leptris_attribute_next(attr)
  end

  yield(:element, name, prefix, uri, ns_buf, attrs_buf, nil, depth)
end

#materialize_fields(native, buffers, &block) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/moxml/adapter/leptris/materialize.rb', line 41

def materialize_fields(native, buffers, &block)
  return nil unless Leptris::BULK_FIELD_READS

  doc = native.is_a?(::Leptris::XML::Document) ? native : native.document
  # Marker-bearing text needs the split pipeline (children-level
  # ER expansion); the bulk path has no marker handling.
  return nil if doc.nil? || attachments.get(doc, :entity_markers)

  root_ptr = if native.is_a?(::Leptris::XML::Document)
               doc.root&.c_ptr
             else
               native.c_ptr
             end
  return nil unless root_ptr

  walk_fields(root_ptr, 0, buffers,
              ::FFI::MemoryPointer.new(:pointer, CHILDREN_CAPACITY),
              &block)
  true
end

#walk_fields(ptr, depth, buffers, child_buf, &block) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/moxml/adapter/leptris/materialize.rb', line 62

def walk_fields(ptr, depth, buffers, child_buf, &block)
  f = BINDING_FFI
  capacity = CHILDREN_CAPACITY
  count = f.leptris_node_children(ptr, child_buf, capacity)
  while count == capacity
    capacity *= 4
    child_buf = ::FFI::MemoryPointer.new(:pointer, capacity)
    count = f.leptris_node_children(ptr, child_buf, capacity)
  end

  # Snapshot before recursing — the recursion reuses this
  # buffer for the next level's child list.
  children = child_buf.read_array_of_pointer(count)

  children.each do |child|
    case f.leptris_node_get_type(child)
    when NODE_ELEMENT
      walk_fields(child, depth + 1, buffers, child_buf, &block)
    when NODE_TEXT
      yield(:text, nil, nil, nil, EMPTY_FIELDS, EMPTY_FIELDS, f.leptris_text_node_get_content(child), depth + 1)
    when NODE_CDATA
      yield(:cdata, nil, nil, nil, EMPTY_FIELDS, EMPTY_FIELDS, f.leptris_cdata_node_get_content(child), depth + 1)
    when NODE_COMMENT
      yield(:comment, nil, nil, nil, EMPTY_FIELDS, EMPTY_FIELDS, f.leptris_comment_node_get_content(child), depth + 1)
    when NODE_PI
      yield(:processing_instruction, f.leptris_pi_node_get_target(child), nil, nil, EMPTY_FIELDS, EMPTY_FIELDS, f.leptris_pi_node_get_data(child), depth + 1)
    end
  end

  emit_element_fields(ptr, buffers, depth, &block)
end