datadog 2.21.0 → 2.23.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.
Files changed (205) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +106 -2
  3. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  4. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  5. data/ext/datadog_profiling_native_extension/collectors_stack.c +4 -0
  6. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  7. data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  9. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  10. data/ext/libdatadog_api/ddsketch.c +106 -0
  11. data/ext/libdatadog_api/feature_flags.c +554 -0
  12. data/ext/libdatadog_api/feature_flags.h +5 -0
  13. data/ext/libdatadog_api/init.c +5 -0
  14. data/ext/libdatadog_api/library_config.c +34 -25
  15. data/ext/libdatadog_api/process_discovery.c +19 -13
  16. data/ext/libdatadog_extconf_helpers.rb +1 -1
  17. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  18. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
  19. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
  20. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  21. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  22. data/lib/datadog/appsec/api_security/route_extractor.rb +23 -6
  23. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  24. data/lib/datadog/appsec/assets/blocked.html +8 -0
  25. data/lib/datadog/appsec/assets/blocked.json +1 -1
  26. data/lib/datadog/appsec/assets/blocked.text +3 -1
  27. data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
  28. data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
  29. data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
  30. data/lib/datadog/appsec/assets.rb +1 -1
  31. data/lib/datadog/appsec/compressed_json.rb +1 -1
  32. data/lib/datadog/appsec/configuration/settings.rb +9 -0
  33. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
  34. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
  35. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
  36. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
  37. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
  38. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
  39. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
  40. data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
  41. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
  42. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
  43. data/lib/datadog/appsec/event.rb +12 -14
  44. data/lib/datadog/appsec/metrics/collector.rb +19 -3
  45. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
  46. data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
  47. data/lib/datadog/appsec/remote.rb +29 -13
  48. data/lib/datadog/appsec/response.rb +18 -4
  49. data/lib/datadog/appsec/security_engine/result.rb +28 -9
  50. data/lib/datadog/appsec/security_engine/runner.rb +17 -7
  51. data/lib/datadog/appsec/security_event.rb +5 -7
  52. data/lib/datadog/core/configuration/components.rb +44 -9
  53. data/lib/datadog/core/configuration/config_helper.rb +1 -1
  54. data/lib/datadog/core/configuration/settings.rb +14 -0
  55. data/lib/datadog/core/configuration/stable_config.rb +10 -0
  56. data/lib/datadog/core/configuration/supported_configurations.rb +330 -299
  57. data/lib/datadog/core/configuration.rb +1 -1
  58. data/lib/datadog/core/ddsketch.rb +19 -0
  59. data/lib/datadog/core/environment/ext.rb +6 -0
  60. data/lib/datadog/core/environment/process.rb +79 -0
  61. data/lib/datadog/core/environment/yjit.rb +2 -1
  62. data/lib/datadog/core/feature_flags.rb +61 -0
  63. data/lib/datadog/core/pin.rb +4 -8
  64. data/lib/datadog/core/process_discovery.rb +4 -2
  65. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  66. data/lib/datadog/core/remote/component.rb +4 -6
  67. data/lib/datadog/core/remote/transport/config.rb +2 -10
  68. data/lib/datadog/core/remote/transport/http/config.rb +9 -9
  69. data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
  70. data/lib/datadog/core/remote/transport/http.rb +2 -0
  71. data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
  72. data/lib/datadog/core/remote/worker.rb +25 -37
  73. data/lib/datadog/core/tag_builder.rb +0 -4
  74. data/lib/datadog/core/tag_normalizer.rb +84 -0
  75. data/lib/datadog/core/telemetry/component.rb +18 -3
  76. data/lib/datadog/core/telemetry/emitter.rb +6 -6
  77. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  78. data/lib/datadog/core/telemetry/event/app_started.rb +52 -49
  79. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
  80. data/lib/datadog/core/telemetry/event.rb +1 -0
  81. data/lib/datadog/core/telemetry/logger.rb +2 -2
  82. data/lib/datadog/core/telemetry/logging.rb +2 -8
  83. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
  84. data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
  85. data/lib/datadog/core/transport/http/client.rb +69 -0
  86. data/lib/datadog/core/transport/response.rb +4 -1
  87. data/lib/datadog/core/utils/array.rb +29 -0
  88. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  89. data/lib/datadog/core/utils/network.rb +22 -1
  90. data/lib/datadog/core/utils/only_once_successful.rb +6 -2
  91. data/lib/datadog/core/utils.rb +2 -0
  92. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  93. data/lib/datadog/data_streams/configuration.rb +11 -0
  94. data/lib/datadog/data_streams/ext.rb +11 -0
  95. data/lib/datadog/data_streams/extensions.rb +16 -0
  96. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  97. data/lib/datadog/data_streams/processor.rb +509 -0
  98. data/lib/datadog/data_streams/transport/http/api.rb +33 -0
  99. data/lib/datadog/data_streams/transport/http/client.rb +21 -0
  100. data/lib/datadog/data_streams/transport/http/stats.rb +87 -0
  101. data/lib/datadog/data_streams/transport/http.rb +41 -0
  102. data/lib/datadog/data_streams/transport/stats.rb +60 -0
  103. data/lib/datadog/data_streams.rb +100 -0
  104. data/lib/datadog/di/boot.rb +1 -0
  105. data/lib/datadog/di/component.rb +14 -16
  106. data/lib/datadog/di/context.rb +70 -0
  107. data/lib/datadog/di/el/compiler.rb +164 -0
  108. data/lib/datadog/di/el/evaluator.rb +159 -0
  109. data/lib/datadog/di/el/expression.rb +42 -0
  110. data/lib/datadog/di/el.rb +5 -0
  111. data/lib/datadog/di/error.rb +29 -0
  112. data/lib/datadog/di/instrumenter.rb +163 -48
  113. data/lib/datadog/di/probe.rb +55 -15
  114. data/lib/datadog/di/probe_builder.rb +39 -1
  115. data/lib/datadog/di/probe_manager.rb +13 -4
  116. data/lib/datadog/di/probe_notification_builder.rb +105 -67
  117. data/lib/datadog/di/proc_responder.rb +32 -0
  118. data/lib/datadog/di/serializer.rb +151 -7
  119. data/lib/datadog/di/transport/diagnostics.rb +2 -2
  120. data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
  121. data/lib/datadog/di/transport/http/input.rb +2 -4
  122. data/lib/datadog/di/transport/http.rb +6 -2
  123. data/lib/datadog/di/transport/input.rb +64 -4
  124. data/lib/datadog/open_feature/component.rb +60 -0
  125. data/lib/datadog/open_feature/configuration.rb +27 -0
  126. data/lib/datadog/open_feature/evaluation_engine.rb +69 -0
  127. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  128. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  129. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  130. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  131. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  132. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  133. data/lib/datadog/open_feature/ext.rb +14 -0
  134. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  135. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  136. data/lib/datadog/open_feature/provider.rb +141 -0
  137. data/lib/datadog/open_feature/remote.rb +74 -0
  138. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  139. data/lib/datadog/open_feature/transport.rb +72 -0
  140. data/lib/datadog/open_feature.rb +19 -0
  141. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  142. data/lib/datadog/opentelemetry/metrics.rb +110 -0
  143. data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
  144. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +38 -0
  145. data/lib/datadog/opentelemetry.rb +3 -0
  146. data/lib/datadog/profiling/collectors/code_provenance.rb +15 -6
  147. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
  148. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  149. data/lib/datadog/profiling/profiler.rb +4 -0
  150. data/lib/datadog/profiling/tag_builder.rb +36 -3
  151. data/lib/datadog/profiling.rb +1 -2
  152. data/lib/datadog/single_step_instrument.rb +1 -1
  153. data/lib/datadog/tracing/component.rb +6 -17
  154. data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
  155. data/lib/datadog/tracing/configuration/ext.rb +9 -0
  156. data/lib/datadog/tracing/configuration/settings.rb +77 -3
  157. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  158. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  159. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  160. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  161. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  162. data/lib/datadog/tracing/contrib/component.rb +2 -2
  163. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  164. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  165. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  166. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  167. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
  168. data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
  169. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +74 -44
  170. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  171. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  172. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  173. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  174. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  175. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  176. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  177. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  178. data/lib/datadog/tracing/contrib/karafka/patcher.rb +32 -0
  179. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  180. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  181. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  182. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  183. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  184. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  185. data/lib/datadog/tracing/contrib/status_range_matcher.rb +7 -0
  186. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  187. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  188. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  189. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  190. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  191. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
  192. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  193. data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
  194. data/lib/datadog/tracing/contrib.rb +1 -0
  195. data/lib/datadog/tracing/metadata/ext.rb +9 -1
  196. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  197. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  198. data/lib/datadog/tracing/transport/traces.rb +3 -5
  199. data/lib/datadog/version.rb +2 -2
  200. data/lib/datadog.rb +2 -0
  201. metadata +92 -16
  202. data/ext/libdatadog_api/macos_development.md +0 -26
  203. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  204. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  205. data/lib/datadog/di/transport/http/client.rb +0 -47
@@ -29,7 +29,11 @@ module Datadog
29
29
  # In https://github.com/DataDog/dd-trace-rb/pull/1398#issuecomment-797378810 we have a discussion of alternatives,
30
30
  # including an alternative implementation that is Ractor-safe once spent.
31
31
  class OnlyOnceSuccessful < OnlyOnce
32
- def initialize(limit = 0)
32
+ def initialize(limit = nil)
33
+ if limit && limit <= 0
34
+ raise ArgumentError, "Limit must be a positive integer if provided: #{limit}"
35
+ end
36
+
33
37
  super()
34
38
 
35
39
  @limit = limit
@@ -71,7 +75,7 @@ module Datadog
71
75
  end
72
76
 
73
77
  def limited?
74
- !@limit.nil? && @limit.positive?
78
+ !@limit.nil?
75
79
  end
76
80
 
77
81
  def reset_ran_once_state_for_tests
@@ -38,6 +38,8 @@ module Datadog
38
38
 
39
39
  # Ensure `str` is a valid UTF-8, ready to be
40
40
  # sent through the tracer transport.
41
+ # DEV-3.0: This method should unconditionally handle invalid byte sequences
42
+ # DEV-3.0: and return a safe string to display.
41
43
  #
42
44
  # @param [String,#to_s] str object to be converted to a UTF-8 string
43
45
  # @param [Boolean] binary whether to expect binary data in the `str` parameter
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../core/environment/variable_helpers'
4
+ require_relative '../ext'
5
+
6
+ module Datadog
7
+ module DataStreams
8
+ module Configuration
9
+ # Configuration settings for Data Streams Monitoring.
10
+ module Settings
11
+ def self.extended(base)
12
+ base = base.singleton_class unless base.is_a?(Class)
13
+ add_settings!(base)
14
+ end
15
+
16
+ def self.add_settings!(base)
17
+ base.class_eval do
18
+ # Data Streams Monitoring configuration
19
+ # @public_api
20
+ settings :data_streams do
21
+ # Whether Data Streams Monitoring is enabled. When enabled, the library will
22
+ # collect and report data lineage information for messaging systems.
23
+ #
24
+ # @default `DD_DATA_STREAMS_ENABLED` environment variable, otherwise `false`.
25
+ # @return [Boolean]
26
+ option :enabled do |o|
27
+ o.type :bool
28
+ o.env Ext::ENV_ENABLED
29
+ o.default false
30
+ end
31
+
32
+ # The interval (in seconds) at which Data Streams Monitoring stats are flushed.
33
+ #
34
+ # @default 10.0
35
+ # @env '_DD_TRACE_STATS_WRITER_INTERVAL'
36
+ # @return [Float]
37
+ # @!visibility private
38
+ option :interval do |o|
39
+ o.type :float
40
+ o.env '_DD_TRACE_STATS_WRITER_INTERVAL'
41
+ o.default 10.0
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configuration/settings'
4
+
5
+ module Datadog
6
+ module DataStreams
7
+ # Configuration for Data Streams Monitoring
8
+ module Configuration
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DataStreams
5
+ # Constants for Data Streams Monitoring configuration
6
+ # @public_api
7
+ module Ext
8
+ ENV_ENABLED = 'DD_DATA_STREAMS_ENABLED'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configuration'
4
+ require_relative '../core/configuration'
5
+
6
+ module Datadog
7
+ module DataStreams
8
+ # Extends Datadog with Data Streams Monitoring features
9
+ module Extensions
10
+ # Inject Data Streams settings into global configuration.
11
+ def self.activate!
12
+ Core::Configuration::Settings.extend(Configuration::Settings)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+ require_relative '../core/utils/base64'
5
+
6
+ module Datadog
7
+ module DataStreams
8
+ # Represents a pathway context for data streams monitoring
9
+ class PathwayContext
10
+ # The current pathway hash value (result of FNV-1a hash function)
11
+ attr_accessor :hash
12
+ # When the pathway started
13
+ attr_accessor :pathway_start
14
+ # When the current edge started
15
+ attr_accessor :current_edge_start
16
+ # The hash value of the parent checkpoint
17
+ attr_accessor :parent_hash
18
+ # The direction tag of the previous checkpoint (e.g., 'direction:in', 'direction:out'), or nil if none
19
+ attr_accessor :previous_direction
20
+ # Hash value of the closest checkpoint in opposite direction (used for loop detection)
21
+ attr_accessor :closest_opposite_direction_hash
22
+ # Edge start time of the closest opposite direction checkpoint
23
+ attr_accessor :closest_opposite_direction_edge_start
24
+
25
+ def initialize(hash_value:, pathway_start:, current_edge_start:)
26
+ @hash = hash_value
27
+ @pathway_start = pathway_start
28
+ @current_edge_start = current_edge_start
29
+ @parent_hash = nil
30
+
31
+ @previous_direction = nil
32
+ @closest_opposite_direction_hash = 0
33
+ @closest_opposite_direction_edge_start = current_edge_start
34
+ end
35
+
36
+ def encode_b64
37
+ Core::Utils::Base64.strict_encode64(encode)
38
+ end
39
+
40
+ # Decode pathway context from base64 encoded string
41
+ def self.decode_b64(encoded_ctx)
42
+ return nil unless encoded_ctx && !encoded_ctx.empty?
43
+
44
+ begin
45
+ binary_data = Core::Utils::Base64.strict_decode64(encoded_ctx)
46
+ decode(binary_data)
47
+ rescue ArgumentError => e
48
+ # Invalid base64 encoding - may indicate version mismatch or corruption
49
+ Datadog.logger.debug("Failed to decode DSM pathway context: #{e.message}")
50
+ nil
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def encode
57
+ # Format:
58
+ # - 8 bytes: hash value (little-endian)
59
+ # - VarInt: pathway start time (milliseconds)
60
+ # - VarInt: current edge start time (milliseconds)
61
+ [@hash].pack('Q') <<
62
+ encode_var_int_64(time_to_ms(@pathway_start)) <<
63
+ encode_var_int_64(time_to_ms(@current_edge_start))
64
+ end
65
+
66
+ # Decode pathway context from binary data
67
+ def self.decode(binary_data)
68
+ return nil unless binary_data && binary_data.bytesize >= 8
69
+
70
+ reader = StringIO.new(binary_data)
71
+
72
+ # Extract 8-byte hash (little-endian)
73
+ hash_bytes = reader.read(8)
74
+ return nil unless hash_bytes
75
+
76
+ hash_value = hash_bytes.unpack1('Q') # : Integer
77
+
78
+ # Extract pathway start time (VarInt milliseconds)
79
+ pathway_start_ms = decode_varint(reader)
80
+ return nil unless pathway_start_ms
81
+
82
+ # Extract current edge start time (VarInt milliseconds)
83
+ current_edge_start_ms = decode_varint(reader)
84
+ return nil unless current_edge_start_ms
85
+
86
+ # Convert milliseconds to Time objects
87
+ pathway_start = ms_to_time(pathway_start_ms)
88
+ current_edge_start = ms_to_time(current_edge_start_ms)
89
+
90
+ new(
91
+ hash_value: hash_value,
92
+ pathway_start: pathway_start,
93
+ current_edge_start: current_edge_start
94
+ )
95
+ rescue EOFError
96
+ # Not enough data in binary stream
97
+ nil
98
+ end
99
+ private_class_method :decode
100
+
101
+ # Encode an unsigned 64-bit integer using LEB128 variable-length encoding.
102
+ #
103
+ # This implements unsigned LEB128 (Little Endian Base 128) encoding as specified
104
+ # in DWARF5 standard section 7.6:
105
+ # https://dwarfstd.org/doc/DWARF5.pdf#page=301
106
+ #
107
+ # Each byte uses 7 bits for data and 1 bit to indicate continuation.
108
+ # The high bit is set if more bytes follow, clear for the final byte.
109
+ #
110
+ # @param value [Integer] Unsigned integer value to encode
111
+ # @return [String] Binary string of encoded bytes
112
+ def encode_var_int_64(value)
113
+ bytes = []
114
+ while value >= 0x80
115
+ bytes << ((value & 0x7F) | 0x80)
116
+ value >>= 7
117
+ end
118
+ bytes << value
119
+ bytes.pack('C*')
120
+ end
121
+
122
+ # Decode an unsigned LEB128 variable-length integer from IO stream.
123
+ #
124
+ # This implements unsigned LEB128 (Little Endian Base 128) decoding as specified
125
+ # in DWARF5 standard section 7.6:
126
+ # https://dwarfstd.org/doc/DWARF5.pdf#page=301
127
+ #
128
+ # VarInt format: Each byte uses 7 bits for data, 1 bit for continuation
129
+ # - High bit set = more bytes follow
130
+ # - High bit clear = final byte
131
+ # - Data bits accumulated in little-endian order
132
+ #
133
+ # @param io [StringIO] IO stream to read from
134
+ # @return [Integer, nil] Decoded unsigned integer, or nil if malformed
135
+ def self.decode_varint(io)
136
+ value = 0
137
+ shift = 0
138
+
139
+ loop do
140
+ byte = io.readbyte
141
+
142
+ # Add this byte's 7 data bits to our value
143
+ value |= (byte & 0x7F) << shift
144
+
145
+ # If high bit is clear, we're done
146
+ return value unless (byte & 0x80).nonzero?
147
+
148
+ shift += 7
149
+
150
+ # Safety: prevent infinite decoding
151
+ return nil if shift >= 64
152
+ end
153
+ rescue EOFError
154
+ # Stream ended unexpectedly - malformed data
155
+ nil
156
+ end
157
+ private_class_method :decode_varint
158
+
159
+ def self.ms_to_time(milliseconds)
160
+ ::Time.at(milliseconds / 1000.0)
161
+ end
162
+ private_class_method :ms_to_time
163
+
164
+ def time_to_ms(time)
165
+ (time.to_f * 1000).to_i
166
+ end
167
+ end
168
+ end
169
+ end