oscura 0.5.0__py3-none-any.whl → 0.6.0__py3-none-any.whl
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.
- oscura/__init__.py +169 -167
- oscura/analyzers/__init__.py +3 -0
- oscura/analyzers/classification.py +659 -0
- oscura/analyzers/digital/__init__.py +0 -48
- oscura/analyzers/digital/edges.py +325 -65
- oscura/analyzers/digital/extraction.py +0 -195
- oscura/analyzers/digital/quality.py +293 -166
- oscura/analyzers/digital/timing.py +260 -115
- oscura/analyzers/digital/timing_numba.py +334 -0
- oscura/analyzers/entropy.py +605 -0
- oscura/analyzers/eye/diagram.py +176 -109
- oscura/analyzers/eye/metrics.py +5 -5
- oscura/analyzers/jitter/__init__.py +6 -4
- oscura/analyzers/jitter/ber.py +52 -52
- oscura/analyzers/jitter/classification.py +156 -0
- oscura/analyzers/jitter/decomposition.py +163 -113
- oscura/analyzers/jitter/spectrum.py +80 -64
- oscura/analyzers/ml/__init__.py +39 -0
- oscura/analyzers/ml/features.py +600 -0
- oscura/analyzers/ml/signal_classifier.py +604 -0
- oscura/analyzers/packet/daq.py +246 -158
- oscura/analyzers/packet/parser.py +12 -1
- oscura/analyzers/packet/payload.py +50 -2110
- oscura/analyzers/packet/payload_analysis.py +361 -181
- oscura/analyzers/packet/payload_patterns.py +133 -70
- oscura/analyzers/packet/stream.py +84 -23
- oscura/analyzers/patterns/__init__.py +26 -5
- oscura/analyzers/patterns/anomaly_detection.py +908 -0
- oscura/analyzers/patterns/clustering.py +169 -108
- oscura/analyzers/patterns/clustering_optimized.py +227 -0
- oscura/analyzers/patterns/discovery.py +1 -1
- oscura/analyzers/patterns/matching.py +581 -197
- oscura/analyzers/patterns/pattern_mining.py +778 -0
- oscura/analyzers/patterns/periodic.py +121 -38
- oscura/analyzers/patterns/sequences.py +175 -78
- oscura/analyzers/power/conduction.py +1 -1
- oscura/analyzers/power/soa.py +6 -6
- oscura/analyzers/power/switching.py +250 -110
- oscura/analyzers/protocol/__init__.py +17 -1
- oscura/analyzers/protocols/__init__.py +1 -22
- oscura/analyzers/protocols/base.py +6 -6
- oscura/analyzers/protocols/ble/__init__.py +38 -0
- oscura/analyzers/protocols/ble/analyzer.py +809 -0
- oscura/analyzers/protocols/ble/uuids.py +288 -0
- oscura/analyzers/protocols/can.py +257 -127
- oscura/analyzers/protocols/can_fd.py +107 -80
- oscura/analyzers/protocols/flexray.py +139 -80
- oscura/analyzers/protocols/hdlc.py +93 -58
- oscura/analyzers/protocols/i2c.py +247 -106
- oscura/analyzers/protocols/i2s.py +138 -86
- oscura/analyzers/protocols/industrial/__init__.py +40 -0
- oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
- oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
- oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
- oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
- oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
- oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
- oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
- oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
- oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
- oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
- oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
- oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
- oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
- oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
- oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
- oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
- oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
- oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
- oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
- oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
- oscura/analyzers/protocols/jtag.py +180 -98
- oscura/analyzers/protocols/lin.py +219 -114
- oscura/analyzers/protocols/manchester.py +4 -4
- oscura/analyzers/protocols/onewire.py +253 -149
- oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
- oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
- oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
- oscura/analyzers/protocols/spi.py +192 -95
- oscura/analyzers/protocols/swd.py +321 -167
- oscura/analyzers/protocols/uart.py +267 -125
- oscura/analyzers/protocols/usb.py +235 -131
- oscura/analyzers/side_channel/power.py +17 -12
- oscura/analyzers/signal/__init__.py +15 -0
- oscura/analyzers/signal/timing_analysis.py +1086 -0
- oscura/analyzers/signal_integrity/__init__.py +4 -1
- oscura/analyzers/signal_integrity/sparams.py +2 -19
- oscura/analyzers/spectral/chunked.py +129 -60
- oscura/analyzers/spectral/chunked_fft.py +300 -94
- oscura/analyzers/spectral/chunked_wavelet.py +100 -80
- oscura/analyzers/statistical/checksum.py +376 -217
- oscura/analyzers/statistical/classification.py +229 -107
- oscura/analyzers/statistical/entropy.py +78 -53
- oscura/analyzers/statistics/correlation.py +407 -211
- oscura/analyzers/statistics/outliers.py +2 -2
- oscura/analyzers/statistics/streaming.py +30 -5
- oscura/analyzers/validation.py +216 -101
- oscura/analyzers/waveform/measurements.py +9 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
- oscura/analyzers/waveform/spectral.py +500 -228
- oscura/api/__init__.py +31 -5
- oscura/api/dsl/__init__.py +582 -0
- oscura/{dsl → api/dsl}/commands.py +43 -76
- oscura/{dsl → api/dsl}/interpreter.py +26 -51
- oscura/{dsl → api/dsl}/parser.py +107 -77
- oscura/{dsl → api/dsl}/repl.py +2 -2
- oscura/api/dsl.py +1 -1
- oscura/{integrations → api/integrations}/__init__.py +1 -1
- oscura/{integrations → api/integrations}/llm.py +201 -102
- oscura/api/operators.py +3 -3
- oscura/api/optimization.py +144 -30
- oscura/api/rest_server.py +921 -0
- oscura/api/server/__init__.py +17 -0
- oscura/api/server/dashboard.py +850 -0
- oscura/api/server/static/README.md +34 -0
- oscura/api/server/templates/base.html +181 -0
- oscura/api/server/templates/export.html +120 -0
- oscura/api/server/templates/home.html +284 -0
- oscura/api/server/templates/protocols.html +58 -0
- oscura/api/server/templates/reports.html +43 -0
- oscura/api/server/templates/session_detail.html +89 -0
- oscura/api/server/templates/sessions.html +83 -0
- oscura/api/server/templates/waveforms.html +73 -0
- oscura/automotive/__init__.py +8 -1
- oscura/automotive/can/__init__.py +10 -0
- oscura/automotive/can/checksum.py +3 -1
- oscura/automotive/can/dbc_generator.py +590 -0
- oscura/automotive/can/message_wrapper.py +121 -74
- oscura/automotive/can/patterns.py +98 -21
- oscura/automotive/can/session.py +292 -56
- oscura/automotive/can/state_machine.py +6 -3
- oscura/automotive/can/stimulus_response.py +97 -75
- oscura/automotive/dbc/__init__.py +10 -2
- oscura/automotive/dbc/generator.py +84 -56
- oscura/automotive/dbc/parser.py +6 -6
- oscura/automotive/dtc/data.json +2763 -0
- oscura/automotive/dtc/database.py +2 -2
- oscura/automotive/flexray/__init__.py +31 -0
- oscura/automotive/flexray/analyzer.py +504 -0
- oscura/automotive/flexray/crc.py +185 -0
- oscura/automotive/flexray/fibex.py +449 -0
- oscura/automotive/j1939/__init__.py +45 -8
- oscura/automotive/j1939/analyzer.py +605 -0
- oscura/automotive/j1939/spns.py +326 -0
- oscura/automotive/j1939/transport.py +306 -0
- oscura/automotive/lin/__init__.py +47 -0
- oscura/automotive/lin/analyzer.py +612 -0
- oscura/automotive/loaders/blf.py +13 -2
- oscura/automotive/loaders/csv_can.py +143 -72
- oscura/automotive/loaders/dispatcher.py +50 -2
- oscura/automotive/loaders/mdf.py +86 -45
- oscura/automotive/loaders/pcap.py +111 -61
- oscura/automotive/uds/__init__.py +4 -0
- oscura/automotive/uds/analyzer.py +725 -0
- oscura/automotive/uds/decoder.py +140 -58
- oscura/automotive/uds/models.py +7 -1
- oscura/automotive/visualization.py +1 -1
- oscura/cli/analyze.py +348 -0
- oscura/cli/batch.py +142 -122
- oscura/cli/benchmark.py +275 -0
- oscura/cli/characterize.py +137 -82
- oscura/cli/compare.py +224 -131
- oscura/cli/completion.py +250 -0
- oscura/cli/config_cmd.py +361 -0
- oscura/cli/decode.py +164 -87
- oscura/cli/export.py +286 -0
- oscura/cli/main.py +115 -31
- oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
- oscura/{onboarding → cli/onboarding}/help.py +80 -58
- oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
- oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
- oscura/cli/progress.py +147 -0
- oscura/cli/shell.py +157 -135
- oscura/cli/validate_cmd.py +204 -0
- oscura/cli/visualize.py +158 -0
- oscura/convenience.py +125 -79
- oscura/core/__init__.py +4 -2
- oscura/core/backend_selector.py +3 -3
- oscura/core/cache.py +126 -15
- oscura/core/cancellation.py +1 -1
- oscura/{config → core/config}/__init__.py +20 -11
- oscura/{config → core/config}/defaults.py +1 -1
- oscura/{config → core/config}/loader.py +7 -5
- oscura/{config → core/config}/memory.py +5 -5
- oscura/{config → core/config}/migration.py +1 -1
- oscura/{config → core/config}/pipeline.py +99 -23
- oscura/{config → core/config}/preferences.py +1 -1
- oscura/{config → core/config}/protocol.py +3 -3
- oscura/{config → core/config}/schema.py +426 -272
- oscura/{config → core/config}/settings.py +1 -1
- oscura/{config → core/config}/thresholds.py +195 -153
- oscura/core/correlation.py +5 -6
- oscura/core/cross_domain.py +0 -2
- oscura/core/debug.py +9 -5
- oscura/{extensibility → core/extensibility}/docs.py +158 -70
- oscura/{extensibility → core/extensibility}/extensions.py +160 -76
- oscura/{extensibility → core/extensibility}/logging.py +1 -1
- oscura/{extensibility → core/extensibility}/measurements.py +1 -1
- oscura/{extensibility → core/extensibility}/plugins.py +1 -1
- oscura/{extensibility → core/extensibility}/templates.py +73 -3
- oscura/{extensibility → core/extensibility}/validation.py +1 -1
- oscura/core/gpu_backend.py +11 -7
- oscura/core/log_query.py +101 -11
- oscura/core/logging.py +126 -54
- oscura/core/logging_advanced.py +5 -5
- oscura/core/memory_limits.py +108 -70
- oscura/core/memory_monitor.py +2 -2
- oscura/core/memory_progress.py +7 -7
- oscura/core/memory_warnings.py +1 -1
- oscura/core/numba_backend.py +13 -13
- oscura/{plugins → core/plugins}/__init__.py +9 -9
- oscura/{plugins → core/plugins}/base.py +7 -7
- oscura/{plugins → core/plugins}/cli.py +3 -3
- oscura/{plugins → core/plugins}/discovery.py +186 -106
- oscura/{plugins → core/plugins}/lifecycle.py +1 -1
- oscura/{plugins → core/plugins}/manager.py +7 -7
- oscura/{plugins → core/plugins}/registry.py +3 -3
- oscura/{plugins → core/plugins}/versioning.py +1 -1
- oscura/core/progress.py +16 -1
- oscura/core/provenance.py +8 -2
- oscura/{schemas → core/schemas}/__init__.py +2 -2
- oscura/core/schemas/bus_configuration.json +322 -0
- oscura/core/schemas/device_mapping.json +182 -0
- oscura/core/schemas/packet_format.json +418 -0
- oscura/core/schemas/protocol_definition.json +363 -0
- oscura/core/types.py +4 -0
- oscura/core/uncertainty.py +3 -3
- oscura/correlation/__init__.py +52 -0
- oscura/correlation/multi_protocol.py +811 -0
- oscura/discovery/auto_decoder.py +117 -35
- oscura/discovery/comparison.py +191 -86
- oscura/discovery/quality_validator.py +155 -68
- oscura/discovery/signal_detector.py +196 -79
- oscura/export/__init__.py +18 -20
- oscura/export/kaitai_struct.py +513 -0
- oscura/export/scapy_layer.py +801 -0
- oscura/export/wireshark/README.md +15 -15
- oscura/export/wireshark/generator.py +1 -1
- oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
- oscura/export/wireshark_dissector.py +746 -0
- oscura/guidance/wizard.py +207 -111
- oscura/hardware/__init__.py +19 -0
- oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
- oscura/{acquisition → hardware/acquisition}/file.py +2 -2
- oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
- oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
- oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
- oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
- oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
- oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
- oscura/hardware/firmware/__init__.py +29 -0
- oscura/hardware/firmware/pattern_recognition.py +874 -0
- oscura/hardware/hal_detector.py +736 -0
- oscura/hardware/security/__init__.py +37 -0
- oscura/hardware/security/side_channel_detector.py +1126 -0
- oscura/inference/__init__.py +4 -0
- oscura/inference/active_learning/README.md +7 -7
- oscura/inference/active_learning/observation_table.py +4 -1
- oscura/inference/alignment.py +216 -123
- oscura/inference/bayesian.py +113 -33
- oscura/inference/crc_reverse.py +101 -55
- oscura/inference/logic.py +6 -2
- oscura/inference/message_format.py +342 -183
- oscura/inference/protocol.py +95 -44
- oscura/inference/protocol_dsl.py +180 -82
- oscura/inference/signal_intelligence.py +1439 -706
- oscura/inference/spectral.py +99 -57
- oscura/inference/state_machine.py +810 -158
- oscura/inference/stream.py +270 -110
- oscura/iot/__init__.py +34 -0
- oscura/iot/coap/__init__.py +32 -0
- oscura/iot/coap/analyzer.py +668 -0
- oscura/iot/coap/options.py +212 -0
- oscura/iot/lorawan/__init__.py +21 -0
- oscura/iot/lorawan/crypto.py +206 -0
- oscura/iot/lorawan/decoder.py +801 -0
- oscura/iot/lorawan/mac_commands.py +341 -0
- oscura/iot/mqtt/__init__.py +27 -0
- oscura/iot/mqtt/analyzer.py +999 -0
- oscura/iot/mqtt/properties.py +315 -0
- oscura/iot/zigbee/__init__.py +31 -0
- oscura/iot/zigbee/analyzer.py +615 -0
- oscura/iot/zigbee/security.py +153 -0
- oscura/iot/zigbee/zcl.py +349 -0
- oscura/jupyter/display.py +125 -45
- oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
- oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
- oscura/jupyter/exploratory/fuzzy.py +746 -0
- oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
- oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
- oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
- oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
- oscura/jupyter/exploratory/sync.py +612 -0
- oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
- oscura/jupyter/magic.py +4 -4
- oscura/{ui → jupyter/ui}/__init__.py +2 -2
- oscura/{ui → jupyter/ui}/formatters.py +3 -3
- oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
- oscura/loaders/__init__.py +171 -63
- oscura/loaders/binary.py +88 -1
- oscura/loaders/chipwhisperer.py +153 -137
- oscura/loaders/configurable.py +208 -86
- oscura/loaders/csv_loader.py +458 -215
- oscura/loaders/hdf5_loader.py +278 -119
- oscura/loaders/lazy.py +87 -54
- oscura/loaders/mmap_loader.py +1 -1
- oscura/loaders/numpy_loader.py +253 -116
- oscura/loaders/pcap.py +226 -151
- oscura/loaders/rigol.py +110 -49
- oscura/loaders/sigrok.py +201 -78
- oscura/loaders/tdms.py +81 -58
- oscura/loaders/tektronix.py +291 -174
- oscura/loaders/touchstone.py +182 -87
- oscura/loaders/vcd.py +215 -117
- oscura/loaders/wav.py +155 -68
- oscura/reporting/__init__.py +9 -7
- oscura/reporting/analyze.py +352 -146
- oscura/reporting/argument_preparer.py +69 -14
- oscura/reporting/auto_report.py +97 -61
- oscura/reporting/batch.py +131 -58
- oscura/reporting/chart_selection.py +57 -45
- oscura/reporting/comparison.py +63 -17
- oscura/reporting/content/executive.py +76 -24
- oscura/reporting/core_formats/multi_format.py +11 -8
- oscura/reporting/engine.py +312 -158
- oscura/reporting/enhanced_reports.py +949 -0
- oscura/reporting/export.py +86 -43
- oscura/reporting/formatting/numbers.py +69 -42
- oscura/reporting/html.py +139 -58
- oscura/reporting/index.py +137 -65
- oscura/reporting/output.py +158 -67
- oscura/reporting/pdf.py +67 -102
- oscura/reporting/plots.py +191 -112
- oscura/reporting/sections.py +88 -47
- oscura/reporting/standards.py +104 -61
- oscura/reporting/summary_generator.py +75 -55
- oscura/reporting/tables.py +138 -54
- oscura/reporting/templates/enhanced/protocol_re.html +525 -0
- oscura/reporting/templates/index.md +13 -13
- oscura/sessions/__init__.py +14 -23
- oscura/sessions/base.py +3 -3
- oscura/sessions/blackbox.py +106 -10
- oscura/sessions/generic.py +2 -2
- oscura/sessions/legacy.py +783 -0
- oscura/side_channel/__init__.py +63 -0
- oscura/side_channel/dpa.py +1025 -0
- oscura/utils/__init__.py +15 -1
- oscura/utils/autodetect.py +1 -5
- oscura/utils/bitwise.py +118 -0
- oscura/{builders → utils/builders}/__init__.py +1 -1
- oscura/{comparison → utils/comparison}/__init__.py +6 -6
- oscura/{comparison → utils/comparison}/compare.py +202 -101
- oscura/{comparison → utils/comparison}/golden.py +83 -63
- oscura/{comparison → utils/comparison}/limits.py +313 -89
- oscura/{comparison → utils/comparison}/mask.py +151 -45
- oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
- oscura/{comparison → utils/comparison}/visualization.py +147 -89
- oscura/{component → utils/component}/__init__.py +3 -3
- oscura/{component → utils/component}/impedance.py +122 -58
- oscura/{component → utils/component}/reactive.py +165 -168
- oscura/{component → utils/component}/transmission_line.py +3 -3
- oscura/{filtering → utils/filtering}/__init__.py +6 -6
- oscura/{filtering → utils/filtering}/base.py +1 -1
- oscura/{filtering → utils/filtering}/convenience.py +2 -2
- oscura/{filtering → utils/filtering}/design.py +169 -93
- oscura/{filtering → utils/filtering}/filters.py +2 -2
- oscura/{filtering → utils/filtering}/introspection.py +2 -2
- oscura/utils/geometry.py +31 -0
- oscura/utils/imports.py +184 -0
- oscura/utils/lazy.py +1 -1
- oscura/{math → utils/math}/__init__.py +2 -2
- oscura/{math → utils/math}/arithmetic.py +114 -48
- oscura/{math → utils/math}/interpolation.py +139 -106
- oscura/utils/memory.py +129 -66
- oscura/utils/memory_advanced.py +92 -9
- oscura/utils/memory_extensions.py +10 -8
- oscura/{optimization → utils/optimization}/__init__.py +1 -1
- oscura/{optimization → utils/optimization}/search.py +2 -2
- oscura/utils/performance/__init__.py +58 -0
- oscura/utils/performance/caching.py +889 -0
- oscura/utils/performance/lsh_clustering.py +333 -0
- oscura/utils/performance/memory_optimizer.py +699 -0
- oscura/utils/performance/optimizations.py +675 -0
- oscura/utils/performance/parallel.py +654 -0
- oscura/utils/performance/profiling.py +661 -0
- oscura/{pipeline → utils/pipeline}/base.py +1 -1
- oscura/{pipeline → utils/pipeline}/composition.py +11 -3
- oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
- oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
- oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
- oscura/{search → utils/search}/__init__.py +3 -3
- oscura/{search → utils/search}/anomaly.py +188 -58
- oscura/utils/search/context.py +294 -0
- oscura/{search → utils/search}/pattern.py +138 -10
- oscura/utils/serial.py +51 -0
- oscura/utils/storage/__init__.py +61 -0
- oscura/utils/storage/database.py +1166 -0
- oscura/{streaming → utils/streaming}/chunked.py +302 -143
- oscura/{streaming → utils/streaming}/progressive.py +1 -1
- oscura/{streaming → utils/streaming}/realtime.py +3 -2
- oscura/{triggering → utils/triggering}/__init__.py +6 -6
- oscura/{triggering → utils/triggering}/base.py +6 -6
- oscura/{triggering → utils/triggering}/edge.py +2 -2
- oscura/{triggering → utils/triggering}/pattern.py +2 -2
- oscura/{triggering → utils/triggering}/pulse.py +115 -74
- oscura/{triggering → utils/triggering}/window.py +2 -2
- oscura/utils/validation.py +32 -0
- oscura/validation/__init__.py +121 -0
- oscura/{compliance → validation/compliance}/__init__.py +5 -5
- oscura/{compliance → validation/compliance}/advanced.py +5 -5
- oscura/{compliance → validation/compliance}/masks.py +1 -1
- oscura/{compliance → validation/compliance}/reporting.py +127 -53
- oscura/{compliance → validation/compliance}/testing.py +114 -52
- oscura/validation/compliance_tests.py +915 -0
- oscura/validation/fuzzer.py +990 -0
- oscura/validation/grammar_tests.py +596 -0
- oscura/validation/grammar_validator.py +904 -0
- oscura/validation/hil_testing.py +977 -0
- oscura/{quality → validation/quality}/__init__.py +4 -4
- oscura/{quality → validation/quality}/ensemble.py +251 -171
- oscura/{quality → validation/quality}/explainer.py +3 -3
- oscura/{quality → validation/quality}/scoring.py +1 -1
- oscura/{quality → validation/quality}/warnings.py +4 -4
- oscura/validation/regression_suite.py +808 -0
- oscura/validation/replay.py +788 -0
- oscura/{testing → validation/testing}/__init__.py +2 -2
- oscura/{testing → validation/testing}/synthetic.py +5 -5
- oscura/visualization/__init__.py +9 -0
- oscura/visualization/accessibility.py +1 -1
- oscura/visualization/annotations.py +64 -67
- oscura/visualization/colors.py +7 -7
- oscura/visualization/digital.py +180 -81
- oscura/visualization/eye.py +236 -85
- oscura/visualization/interactive.py +320 -143
- oscura/visualization/jitter.py +587 -247
- oscura/visualization/layout.py +169 -134
- oscura/visualization/optimization.py +103 -52
- oscura/visualization/palettes.py +1 -1
- oscura/visualization/power.py +427 -211
- oscura/visualization/power_extended.py +626 -297
- oscura/visualization/presets.py +2 -0
- oscura/visualization/protocols.py +495 -181
- oscura/visualization/render.py +79 -63
- oscura/visualization/reverse_engineering.py +171 -124
- oscura/visualization/signal_integrity.py +460 -279
- oscura/visualization/specialized.py +190 -100
- oscura/visualization/spectral.py +670 -255
- oscura/visualization/thumbnails.py +166 -137
- oscura/visualization/waveform.py +150 -63
- oscura/workflows/__init__.py +3 -0
- oscura/{batch → workflows/batch}/__init__.py +5 -5
- oscura/{batch → workflows/batch}/advanced.py +150 -75
- oscura/workflows/batch/aggregate.py +531 -0
- oscura/workflows/batch/analyze.py +236 -0
- oscura/{batch → workflows/batch}/logging.py +2 -2
- oscura/{batch → workflows/batch}/metrics.py +1 -1
- oscura/workflows/complete_re.py +1144 -0
- oscura/workflows/compliance.py +44 -54
- oscura/workflows/digital.py +197 -51
- oscura/workflows/legacy/__init__.py +12 -0
- oscura/{workflow → workflows/legacy}/dag.py +4 -1
- oscura/workflows/multi_trace.py +9 -9
- oscura/workflows/power.py +42 -62
- oscura/workflows/protocol.py +82 -49
- oscura/workflows/reverse_engineering.py +351 -150
- oscura/workflows/signal_integrity.py +157 -82
- oscura-0.6.0.dist-info/METADATA +643 -0
- oscura-0.6.0.dist-info/RECORD +590 -0
- oscura/analyzers/digital/ic_database.py +0 -498
- oscura/analyzers/digital/timing_paths.py +0 -339
- oscura/analyzers/digital/vintage.py +0 -377
- oscura/analyzers/digital/vintage_result.py +0 -148
- oscura/analyzers/protocols/parallel_bus.py +0 -449
- oscura/batch/aggregate.py +0 -300
- oscura/batch/analyze.py +0 -139
- oscura/dsl/__init__.py +0 -73
- oscura/exceptions.py +0 -59
- oscura/exploratory/fuzzy.py +0 -513
- oscura/exploratory/sync.py +0 -384
- oscura/export/wavedrom.py +0 -430
- oscura/exporters/__init__.py +0 -94
- oscura/exporters/csv.py +0 -303
- oscura/exporters/exporters.py +0 -44
- oscura/exporters/hdf5.py +0 -217
- oscura/exporters/html_export.py +0 -701
- oscura/exporters/json_export.py +0 -338
- oscura/exporters/markdown_export.py +0 -367
- oscura/exporters/matlab_export.py +0 -354
- oscura/exporters/npz_export.py +0 -219
- oscura/exporters/spice_export.py +0 -210
- oscura/exporters/vintage_logic_csv.py +0 -247
- oscura/reporting/vintage_logic_report.py +0 -523
- oscura/search/context.py +0 -149
- oscura/session/__init__.py +0 -34
- oscura/session/annotations.py +0 -289
- oscura/session/history.py +0 -313
- oscura/session/session.py +0 -520
- oscura/visualization/digital_advanced.py +0 -718
- oscura/visualization/figure_manager.py +0 -156
- oscura/workflow/__init__.py +0 -13
- oscura-0.5.0.dist-info/METADATA +0 -407
- oscura-0.5.0.dist-info/RECORD +0 -486
- /oscura/core/{config.py → config/legacy.py} +0 -0
- /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
- /oscura/{extensibility → core/extensibility}/registry.py +0 -0
- /oscura/{plugins → core/plugins}/isolation.py +0 -0
- /oscura/{builders → utils/builders}/signal_builder.py +0 -0
- /oscura/{optimization → utils/optimization}/parallel.py +0 -0
- /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
- /oscura/{streaming → utils/streaming}/__init__.py +0 -0
- {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
- {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
oscura/inference/protocol.py
CHANGED
|
@@ -83,7 +83,23 @@ def detect_protocol(
|
|
|
83
83
|
characteristics = _analyze_signal_characteristics(trace)
|
|
84
84
|
|
|
85
85
|
# Define protocol detectors
|
|
86
|
-
protocol_detectors
|
|
86
|
+
protocol_detectors = _build_protocol_detectors(characteristics)
|
|
87
|
+
|
|
88
|
+
# Score protocols
|
|
89
|
+
candidates = _score_protocols(protocol_detectors, characteristics, parallel)
|
|
90
|
+
|
|
91
|
+
# Validate and select primary
|
|
92
|
+
primary = _validate_detection(candidates, min_confidence)
|
|
93
|
+
|
|
94
|
+
# Build result
|
|
95
|
+
return _build_detection_result(primary, characteristics, candidates, return_candidates)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _build_protocol_detectors(
|
|
99
|
+
characteristics: dict[str, Any],
|
|
100
|
+
) -> list[tuple[str, Callable[[dict[str, Any]], float], dict[str, Any]]]:
|
|
101
|
+
"""Build list of protocol detectors with configurations."""
|
|
102
|
+
return [
|
|
87
103
|
(
|
|
88
104
|
"UART",
|
|
89
105
|
_score_uart,
|
|
@@ -121,65 +137,100 @@ def detect_protocol(
|
|
|
121
137
|
),
|
|
122
138
|
]
|
|
123
139
|
|
|
124
|
-
# Score protocols (optionally in parallel)
|
|
125
|
-
candidates = []
|
|
126
140
|
|
|
141
|
+
def _score_protocols(
|
|
142
|
+
protocol_detectors: list[tuple[str, Callable[[dict[str, Any]], float], dict[str, Any]]],
|
|
143
|
+
characteristics: dict[str, Any],
|
|
144
|
+
parallel: bool,
|
|
145
|
+
) -> list[dict[str, Any]]:
|
|
146
|
+
"""Score all protocol detectors."""
|
|
127
147
|
if parallel:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
148
|
+
return _score_protocols_parallel(protocol_detectors, characteristics)
|
|
149
|
+
return _score_protocols_sequential(protocol_detectors, characteristics)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _score_protocols_parallel(
|
|
153
|
+
protocol_detectors: list[tuple[str, Callable[[dict[str, Any]], float], dict[str, Any]]],
|
|
154
|
+
characteristics: dict[str, Any],
|
|
155
|
+
) -> list[dict[str, Any]]:
|
|
156
|
+
"""Score protocols in parallel using ThreadPoolExecutor."""
|
|
157
|
+
candidates = []
|
|
158
|
+
|
|
159
|
+
with ThreadPoolExecutor(max_workers=len(protocol_detectors)) as executor:
|
|
160
|
+
future_to_protocol = {
|
|
161
|
+
executor.submit(scorer, characteristics): (name, config)
|
|
162
|
+
for name, scorer, config in protocol_detectors
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for future in as_completed(future_to_protocol):
|
|
166
|
+
name, config = future_to_protocol[future]
|
|
167
|
+
try:
|
|
168
|
+
score = future.result()
|
|
169
|
+
if score > 0:
|
|
170
|
+
candidates.append(
|
|
171
|
+
{
|
|
172
|
+
"protocol": name,
|
|
173
|
+
"confidence": score,
|
|
174
|
+
"config": config,
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
except Exception:
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
candidates.sort(key=lambda x: x["confidence"], reverse=True) # type: ignore[arg-type, return-value]
|
|
181
|
+
return candidates
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _score_protocols_sequential(
|
|
185
|
+
protocol_detectors: list[tuple[str, Callable[[dict[str, Any]], float], dict[str, Any]]],
|
|
186
|
+
characteristics: dict[str, Any],
|
|
187
|
+
) -> list[dict[str, Any]]:
|
|
188
|
+
"""Score protocols sequentially."""
|
|
189
|
+
candidates = []
|
|
190
|
+
|
|
191
|
+
for name, scorer, config in protocol_detectors:
|
|
192
|
+
score = scorer(characteristics)
|
|
193
|
+
if score > 0:
|
|
194
|
+
candidates.append(
|
|
195
|
+
{
|
|
196
|
+
"protocol": name,
|
|
197
|
+
"confidence": score,
|
|
198
|
+
"config": config,
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
|
|
166
202
|
candidates.sort(key=lambda x: x["confidence"], reverse=True) # type: ignore[arg-type, return-value]
|
|
203
|
+
return candidates
|
|
167
204
|
|
|
205
|
+
|
|
206
|
+
def _validate_detection(
|
|
207
|
+
candidates: list[dict[str, Any]],
|
|
208
|
+
min_confidence: float,
|
|
209
|
+
) -> dict[str, Any]:
|
|
210
|
+
"""Validate that detection meets confidence threshold."""
|
|
168
211
|
if not candidates:
|
|
169
212
|
raise AnalysisError(
|
|
170
213
|
"Could not detect protocol type. Signal may be analog or unsupported protocol."
|
|
171
214
|
)
|
|
172
215
|
|
|
173
|
-
# Primary detection
|
|
174
216
|
primary = candidates[0]
|
|
175
217
|
|
|
176
|
-
if primary["confidence"] < min_confidence:
|
|
218
|
+
if float(primary["confidence"]) < min_confidence:
|
|
177
219
|
raise AnalysisError(
|
|
178
220
|
f"Protocol detection confidence too low: {primary['confidence']:.1%} "
|
|
179
221
|
f"(minimum: {min_confidence:.1%}). Try specifying protocol manually."
|
|
180
222
|
)
|
|
181
223
|
|
|
182
|
-
|
|
224
|
+
return primary
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _build_detection_result(
|
|
228
|
+
primary: dict[str, Any],
|
|
229
|
+
characteristics: dict[str, Any],
|
|
230
|
+
candidates: list[dict[str, Any]],
|
|
231
|
+
return_candidates: bool,
|
|
232
|
+
) -> dict[str, Any]:
|
|
233
|
+
"""Build detection result dictionary."""
|
|
183
234
|
result = {
|
|
184
235
|
"protocol": primary["protocol"],
|
|
185
236
|
"confidence": primary["confidence"],
|
oscura/inference/protocol_dsl.py
CHANGED
|
@@ -512,6 +512,79 @@ class ProtocolDecoder:
|
|
|
512
512
|
return None
|
|
513
513
|
return idx
|
|
514
514
|
|
|
515
|
+
def _decode_bytes_field(
|
|
516
|
+
self, data: bytes, field: FieldDefinition, context: dict[str, Any]
|
|
517
|
+
) -> tuple[bytes, int]:
|
|
518
|
+
"""Decode bytes field.
|
|
519
|
+
|
|
520
|
+
Args:
|
|
521
|
+
data: Binary data
|
|
522
|
+
field: Field definition
|
|
523
|
+
context: Previously decoded fields
|
|
524
|
+
|
|
525
|
+
Returns:
|
|
526
|
+
Tuple of (bytes value, bytes consumed)
|
|
527
|
+
"""
|
|
528
|
+
size = self._resolve_size(field.size, context, data)
|
|
529
|
+
if size > len(data):
|
|
530
|
+
size = len(data) # Use remaining data
|
|
531
|
+
return bytes(data[:size]), size
|
|
532
|
+
|
|
533
|
+
def _decode_string_field(
|
|
534
|
+
self, data: bytes, field: FieldDefinition, context: dict[str, Any]
|
|
535
|
+
) -> tuple[str, int]:
|
|
536
|
+
"""Decode string field.
|
|
537
|
+
|
|
538
|
+
Args:
|
|
539
|
+
data: Binary data
|
|
540
|
+
field: Field definition
|
|
541
|
+
context: Previously decoded fields
|
|
542
|
+
|
|
543
|
+
Returns:
|
|
544
|
+
Tuple of (string value, bytes consumed)
|
|
545
|
+
"""
|
|
546
|
+
size = self._resolve_size(field.size, context, data)
|
|
547
|
+
if size > len(data):
|
|
548
|
+
size = len(data) # Use remaining data
|
|
549
|
+
string_bytes = data[:size]
|
|
550
|
+
|
|
551
|
+
# Try to decode as UTF-8, fall back to latin-1
|
|
552
|
+
try:
|
|
553
|
+
value = string_bytes.decode("utf-8").rstrip("\x00")
|
|
554
|
+
except UnicodeDecodeError:
|
|
555
|
+
value = string_bytes.decode("latin-1").rstrip("\x00")
|
|
556
|
+
|
|
557
|
+
return value, size
|
|
558
|
+
|
|
559
|
+
def _decode_bitfield_field(
|
|
560
|
+
self, data: bytes, field: FieldDefinition, endian: str
|
|
561
|
+
) -> tuple[int, int]:
|
|
562
|
+
"""Decode bitfield field.
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
data: Binary data
|
|
566
|
+
field: Field definition
|
|
567
|
+
endian: Endianness marker
|
|
568
|
+
|
|
569
|
+
Returns:
|
|
570
|
+
Tuple of (bitfield value, bytes consumed)
|
|
571
|
+
|
|
572
|
+
Raises:
|
|
573
|
+
ValueError: If bitfield size is unsupported
|
|
574
|
+
"""
|
|
575
|
+
field_size = field.size if isinstance(field.size, int) else 1
|
|
576
|
+
|
|
577
|
+
if field_size == 1:
|
|
578
|
+
bitfield_value = int(data[0])
|
|
579
|
+
elif field_size == 2:
|
|
580
|
+
bitfield_value = struct.unpack(f"{endian}H", data[:2])[0]
|
|
581
|
+
elif field_size == 4:
|
|
582
|
+
bitfield_value = struct.unpack(f"{endian}I", data[:4])[0]
|
|
583
|
+
else:
|
|
584
|
+
raise ValueError(f"Unsupported bitfield size: {field_size}")
|
|
585
|
+
|
|
586
|
+
return bitfield_value, field_size
|
|
587
|
+
|
|
515
588
|
def _decode_field(
|
|
516
589
|
self, data: bytes, field: FieldDefinition, context: dict[str, Any]
|
|
517
590
|
) -> tuple[Any, int]:
|
|
@@ -538,55 +611,30 @@ class ProtocolDecoder:
|
|
|
538
611
|
return self._decode_integer(data, field_type, endian)
|
|
539
612
|
|
|
540
613
|
# Float types
|
|
541
|
-
|
|
614
|
+
if field_type in ["float32", "float64"]:
|
|
542
615
|
return self._decode_float(data, field_type, endian)
|
|
543
616
|
|
|
544
617
|
# Bytes
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
if size > len(data):
|
|
548
|
-
size = len(data) # Use remaining data
|
|
549
|
-
return bytes(data[:size]), size
|
|
618
|
+
if field_type == "bytes":
|
|
619
|
+
return self._decode_bytes_field(data, field, context)
|
|
550
620
|
|
|
551
621
|
# String
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
if size > len(data):
|
|
555
|
-
size = len(data) # Use remaining data
|
|
556
|
-
string_bytes = data[:size]
|
|
557
|
-
# Try to decode as UTF-8, fall back to latin-1
|
|
558
|
-
try:
|
|
559
|
-
value = string_bytes.decode("utf-8").rstrip("\x00")
|
|
560
|
-
except UnicodeDecodeError:
|
|
561
|
-
value = string_bytes.decode("latin-1").rstrip("\x00")
|
|
562
|
-
return value, size
|
|
622
|
+
if field_type == "string":
|
|
623
|
+
return self._decode_string_field(data, field, context)
|
|
563
624
|
|
|
564
625
|
# Bitfield
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
field_size = field.size if isinstance(field.size, int) else 1
|
|
568
|
-
if field_size == 1:
|
|
569
|
-
bitfield_value = int(data[0])
|
|
570
|
-
elif field_size == 2:
|
|
571
|
-
bitfield_value = struct.unpack(f"{endian}H", data[:2])[0]
|
|
572
|
-
elif field_size == 4:
|
|
573
|
-
bitfield_value = struct.unpack(f"{endian}I", data[:4])[0]
|
|
574
|
-
else:
|
|
575
|
-
raise ValueError(f"Unsupported bitfield size: {field_size}")
|
|
576
|
-
|
|
577
|
-
# Return as-is, caller can extract specific bits
|
|
578
|
-
return bitfield_value, field_size
|
|
626
|
+
if field_type == "bitfield":
|
|
627
|
+
return self._decode_bitfield_field(data, field, endian)
|
|
579
628
|
|
|
580
629
|
# Array
|
|
581
|
-
|
|
630
|
+
if field_type == "array":
|
|
582
631
|
return self._decode_array(data, field, context)
|
|
583
632
|
|
|
584
633
|
# Struct (nested)
|
|
585
|
-
|
|
634
|
+
if field_type == "struct":
|
|
586
635
|
return self._decode_struct(data, field, context)
|
|
587
636
|
|
|
588
|
-
|
|
589
|
-
raise ValueError(f"Unknown field type: {field_type}")
|
|
637
|
+
raise ValueError(f"Unknown field type: {field_type}")
|
|
590
638
|
|
|
591
639
|
def _decode_array(
|
|
592
640
|
self, data: bytes, field: FieldDefinition, context: dict[str, Any]
|
|
@@ -910,71 +958,121 @@ class ProtocolEncoder:
|
|
|
910
958
|
def _encode_field(self, value: Any, field: FieldDefinition) -> bytes:
|
|
911
959
|
"""Encode single field value.
|
|
912
960
|
|
|
913
|
-
: Field encoding.
|
|
914
|
-
|
|
915
961
|
Args:
|
|
916
|
-
value: Field value
|
|
917
|
-
field: Field definition
|
|
962
|
+
value: Field value.
|
|
963
|
+
field: Field definition.
|
|
918
964
|
|
|
919
965
|
Returns:
|
|
920
|
-
Encoded bytes
|
|
966
|
+
Encoded bytes.
|
|
921
967
|
|
|
922
968
|
Raises:
|
|
923
|
-
ValueError: If bytes value is invalid or field type is unknown for encoding
|
|
969
|
+
ValueError: If bytes value is invalid or field type is unknown for encoding.
|
|
970
|
+
|
|
971
|
+
Example:
|
|
972
|
+
>>> encoder._encode_field(42, FieldDefinition("counter", "uint16", "big"))
|
|
973
|
+
b'\\x00*'
|
|
974
|
+
"""
|
|
975
|
+
field_type = field.field_type
|
|
976
|
+
|
|
977
|
+
# Dispatch to type-specific encoders
|
|
978
|
+
if field_type in {"uint8", "int8", "uint16", "int16", "uint32", "int32", "uint64", "int64"}:
|
|
979
|
+
return self._encode_integer_field(value, field)
|
|
980
|
+
elif field_type in {"float32", "float64"}:
|
|
981
|
+
return self._encode_float_field(value, field)
|
|
982
|
+
elif field_type == "bytes":
|
|
983
|
+
return self._encode_bytes_field(value)
|
|
984
|
+
elif field_type == "string":
|
|
985
|
+
return self._encode_string_field(value)
|
|
986
|
+
elif field_type == "array":
|
|
987
|
+
return self._encode_array(value, field)
|
|
988
|
+
elif field_type == "struct":
|
|
989
|
+
return self._encode_struct(value, field)
|
|
990
|
+
else:
|
|
991
|
+
raise ValueError(f"Unknown field type for encoding: {field_type}")
|
|
992
|
+
|
|
993
|
+
def _encode_integer_field(self, value: Any, field: FieldDefinition) -> bytes:
|
|
994
|
+
"""Encode integer field types.
|
|
995
|
+
|
|
996
|
+
Args:
|
|
997
|
+
value: Integer value.
|
|
998
|
+
field: Field definition with type and endianness.
|
|
999
|
+
|
|
1000
|
+
Returns:
|
|
1001
|
+
Packed integer bytes.
|
|
924
1002
|
"""
|
|
925
1003
|
endian = self._endian_map.get(field.endian, ">")
|
|
926
1004
|
field_type = field.field_type
|
|
927
1005
|
|
|
928
|
-
#
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
elif field_type == "int32":
|
|
940
|
-
return struct.pack(f"{endian}i", int(value))
|
|
941
|
-
elif field_type == "uint64":
|
|
942
|
-
return struct.pack(f"{endian}Q", int(value))
|
|
943
|
-
elif field_type == "int64":
|
|
944
|
-
return struct.pack(f"{endian}q", int(value))
|
|
1006
|
+
# Map field types to struct format characters
|
|
1007
|
+
_INT_FORMATS = {
|
|
1008
|
+
"uint8": "B",
|
|
1009
|
+
"int8": "b",
|
|
1010
|
+
"uint16": "H",
|
|
1011
|
+
"int16": "h",
|
|
1012
|
+
"uint32": "I",
|
|
1013
|
+
"int32": "i",
|
|
1014
|
+
"uint64": "Q",
|
|
1015
|
+
"int64": "q",
|
|
1016
|
+
}
|
|
945
1017
|
|
|
946
|
-
|
|
947
|
-
|
|
1018
|
+
fmt_char = _INT_FORMATS[field_type]
|
|
1019
|
+
if fmt_char in {"B", "b"}:
|
|
1020
|
+
# uint8/int8 have no endianness
|
|
1021
|
+
return struct.pack(fmt_char, int(value))
|
|
1022
|
+
else:
|
|
1023
|
+
return struct.pack(f"{endian}{fmt_char}", int(value))
|
|
1024
|
+
|
|
1025
|
+
def _encode_float_field(self, value: Any, field: FieldDefinition) -> bytes:
|
|
1026
|
+
"""Encode floating-point field types.
|
|
1027
|
+
|
|
1028
|
+
Args:
|
|
1029
|
+
value: Float value.
|
|
1030
|
+
field: Field definition with type and endianness.
|
|
1031
|
+
|
|
1032
|
+
Returns:
|
|
1033
|
+
Packed float bytes.
|
|
1034
|
+
"""
|
|
1035
|
+
endian = self._endian_map.get(field.endian, ">")
|
|
1036
|
+
|
|
1037
|
+
if field.field_type == "float32":
|
|
948
1038
|
return struct.pack(f"{endian}f", float(value))
|
|
949
|
-
elif field_type == "float64":
|
|
1039
|
+
elif field.field_type == "float64":
|
|
950
1040
|
return struct.pack(f"{endian}d", float(value))
|
|
1041
|
+
else:
|
|
1042
|
+
raise ValueError(f"Unknown float type: {field.field_type}")
|
|
951
1043
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
if isinstance(value, bytes):
|
|
955
|
-
return value
|
|
956
|
-
elif isinstance(value, list | tuple):
|
|
957
|
-
return bytes(value)
|
|
958
|
-
else:
|
|
959
|
-
raise ValueError(f"Invalid bytes value: {value}")
|
|
1044
|
+
def _encode_bytes_field(self, value: Any) -> bytes:
|
|
1045
|
+
"""Encode bytes field.
|
|
960
1046
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
if isinstance(value, str):
|
|
964
|
-
return value.encode("utf-8")
|
|
965
|
-
else:
|
|
966
|
-
return bytes(value)
|
|
1047
|
+
Args:
|
|
1048
|
+
value: Bytes, list, or tuple of byte values.
|
|
967
1049
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
return self._encode_array(value, field)
|
|
1050
|
+
Returns:
|
|
1051
|
+
Byte sequence.
|
|
971
1052
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1053
|
+
Raises:
|
|
1054
|
+
ValueError: If value cannot be converted to bytes.
|
|
1055
|
+
"""
|
|
1056
|
+
if isinstance(value, bytes):
|
|
1057
|
+
return value
|
|
1058
|
+
elif isinstance(value, list | tuple):
|
|
1059
|
+
return bytes(value)
|
|
1060
|
+
else:
|
|
1061
|
+
raise ValueError(f"Invalid bytes value: {value}")
|
|
975
1062
|
|
|
1063
|
+
def _encode_string_field(self, value: Any) -> bytes:
|
|
1064
|
+
"""Encode string field.
|
|
1065
|
+
|
|
1066
|
+
Args:
|
|
1067
|
+
value: String or bytes.
|
|
1068
|
+
|
|
1069
|
+
Returns:
|
|
1070
|
+
UTF-8 encoded bytes.
|
|
1071
|
+
"""
|
|
1072
|
+
if isinstance(value, str):
|
|
1073
|
+
return value.encode("utf-8")
|
|
976
1074
|
else:
|
|
977
|
-
|
|
1075
|
+
return bytes(value)
|
|
978
1076
|
|
|
979
1077
|
def _encode_array(self, value: list[Any], field: FieldDefinition) -> bytes:
|
|
980
1078
|
"""Encode array field.
|