From 776215db7f99c3fb21d9b08067dc672bdfcbdf54 Mon Sep 17 00:00:00 2001 From: Tigran Najaryan Date: Wed, 7 Oct 2026 17:09:58 +0000 Subject: [PATCH] Add OTLP logs support Extend the OpenTelemetry STEF schema with a Logs root, LogRecord fields, and the complete OTLP severity number enum. Regenerate the Go and Java record, encoder, decoder, reader, writer, dictionary, and state implementations for the new signal. Add a pdata OTLP-to-STEF logs converter with unsorted and sorted modes. Preserve resource, scope, body, attributes, timestamps, severity, trace context, flags, and event names; group equivalent resources and scopes in sorted mode and order records deterministically. Make shared attribute comparison stable across map iteration and support double, byte, map, and nested values. Expose AnyValue conversion for log bodies and fix nested map indexing so all entries are retained. Cover full log-record conversion and sorted grouping, and add the astronomy multipart log fixture plus a size benchmark comparing OTLP with sorted and unsorted STEF under uncompressed and Zstd modes. Tests: CGO_ENABLED=0 go test ./... (go/pdata) Tests: CGO_ENABLED=0 go test -run '^TestLogsMultipart$' -count=1 (benchmarks) Tests: CGO_ENABLED=0 ./gradlew clean test --tests com.example.otelstef.LogsWriterTest (java) --- benchmarks/benchmarks_test.go | 192 +- benchmarks/charts.go | 12 +- benchmarks/cmd/otlp2stef/main.go | 2 +- benchmarks/correctness_test.go | 43 +- benchmarks/encodings/log_encoding.go | 12 + benchmarks/encodings/otlp/logs.go | 37 + .../encodings/otlp/{otlp.go => metrics.go} | 24 +- benchmarks/encodings/stef/logs.go | 77 + benchmarks/generators/file.go | 2 +- benchmarks/otlp2sortedtree_test.go | 2 +- benchmarks/readwrite_test.go | 6 +- benchmarks/size_test.go | 130 +- benchmarks/testdata/astronomy-otellogs.zst | Bin 0 -> 499276 bytes benchmarks/testutils/testutils.go | 24 +- docs/benchmarks.html | 213 ++- examples/ints/internal/ints/modifiedfields.go | 7 + examples/ints/internal/ints/record.go | 27 +- .../jsonl/internal/jsonstef/jsonobject.go | 17 + examples/jsonl/internal/jsonstef/jsonvalue.go | 60 +- .../jsonl/internal/jsonstef/jsonvaluearray.go | 17 + .../jsonl/internal/jsonstef/modifiedfields.go | 7 + examples/jsonl/internal/jsonstef/record.go | 32 +- examples/profile/internal/profile/function.go | 37 +- examples/profile/internal/profile/labels.go | 17 + .../profile/internal/profile/labelvalue.go | 41 +- examples/profile/internal/profile/line.go | 45 +- .../profile/internal/profile/linearray.go | 17 + examples/profile/internal/profile/location.go | 55 +- .../profile/internal/profile/locationarray.go | 23 +- examples/profile/internal/profile/mapping.go | 37 +- .../internal/profile/modifiedfields.go | 7 + examples/profile/internal/profile/numvalue.go | 27 +- .../internal/profile/profilemetadata.go | 69 +- examples/profile/internal/profile/sample.go | 43 +- .../profile/internal/profile/samplevalue.go | 43 +- .../internal/profile/samplevaluearray.go | 17 + .../internal/profile/samplevaluetype.go | 37 +- .../profile/internal/profile/stringarray.go | 11 + go/otel/otel.stef | 57 +- go/otel/otelstef/anyvalue.go | 64 +- go/otel/otelstef/anyvaluearray.go | 17 + go/otel/otelstef/attributes.go | 17 + go/otel/otelstef/clone_test.go | 74 + go/otel/otelstef/common.go | 16 +- go/otel/otelstef/envelope.go | 31 +- go/otel/otelstef/envelopeattributes.go | 15 + go/otel/otelstef/event.go | 31 +- go/otel/otelstef/eventarray.go | 17 + go/otel/otelstef/exemplar.go | 35 +- go/otel/otelstef/exemplararray.go | 17 + go/otel/otelstef/exemplarvalue.go | 35 +- go/otel/otelstef/exphistogrambuckets.go | 31 +- go/otel/otelstef/exphistogramvalue.go | 36 +- go/otel/otelstef/float64array.go | 11 + go/otel/otelstef/histogramvalue.go | 32 +- go/otel/otelstef/keyvaluelist.go | 17 + go/otel/otelstef/link.go | 31 +- go/otel/otelstef/linkarray.go | 17 + go/otel/otelstef/logrecord.go | 1541 +++++++++++++++++ go/otel/otelstef/logs.go | 1027 +++++++++++ go/otel/otelstef/logsreader.go | 140 ++ go/otel/otelstef/logswriter.go | 210 +++ go/otel/otelstef/logswriter_test.go | 251 +++ go/otel/otelstef/metric.go | 45 +- go/otel/otelstef/metrics.go | 82 +- go/otel/otelstef/modifiedfields.go | 7 + go/otel/otelstef/point.go | 35 +- go/otel/otelstef/pointvalue.go | 59 +- go/otel/otelstef/quantilevalue.go | 27 +- go/otel/otelstef/quantilevaluearray.go | 17 + go/otel/otelstef/readerstate.go | 8 + go/otel/otelstef/resource.go | 41 +- go/otel/otelstef/scope.go | 41 +- go/otel/otelstef/severitynumber.go | 32 + go/otel/otelstef/span.go | 43 +- go/otel/otelstef/spans.go | 64 +- go/otel/otelstef/spanstatus.go | 27 +- go/otel/otelstef/summaryvalue.go | 31 +- go/otel/otelstef/uint64array.go | 11 + go/otel/otelstef/writerstate.go | 8 + go/pdata/internal/otlptools/compare.go | 105 +- go/pdata/internal/otlptools/otlpval2tef.go | 6 + go/pdata/internal/otlptools/tef2otlpval.go | 8 +- go/pdata/logs/otlp2stef.go | 20 + go/pdata/logs/otlp2stef_unsorted.go | 123 ++ go/pdata/logs/otlp2stef_unsorted_test.go | 106 ++ go/pdata/logs/sortedbyresource/converter.go | 58 + .../logs/sortedbyresource/converter_test.go | 44 + go/pdata/logs/sortedbyresource/sortedlogs.go | 117 ++ go/pdata/logs/stef2otlp.go | 14 + go/pdata/logs/stef2otlp_unsorted.go | 117 ++ go/pdata/logs/testtools/normalize.go | 112 ++ go/pdata/logs/testtools/normalize_test.go | 64 + go/pdata/metrics/pdatasort.go | 9 + .../sortedbyresource/sortedresource.go | 12 +- go/pkg/types.go | 9 +- .../java/com/example/otelstef/AnyValue.java | 38 +- .../otelstef/AnyValueArrayEncoder.java | 15 +- .../com/example/otelstef/AnyValueEncoder.java | 10 +- .../java/com/example/otelstef/Attributes.java | 1 + .../example/otelstef/AttributesEncoder.java | 20 +- .../example/otelstef/CommonFieldCounts.java | 14 + .../java/com/example/otelstef/Envelope.java | 3 +- .../example/otelstef/EnvelopeAttributes.java | 1 + .../otelstef/EnvelopeAttributesEncoder.java | 18 +- .../com/example/otelstef/EnvelopeEncoder.java | 15 +- .../main/java/com/example/otelstef/Event.java | 6 +- .../example/otelstef/EventArrayEncoder.java | 15 +- .../com/example/otelstef/EventEncoder.java | 15 +- .../java/com/example/otelstef/Exemplar.java | 7 +- .../otelstef/ExemplarArrayEncoder.java | 15 +- .../com/example/otelstef/ExemplarEncoder.java | 17 +- .../com/example/otelstef/ExemplarValue.java | 18 +- .../otelstef/ExemplarValueEncoder.java | 6 + .../example/otelstef/ExpHistogramBuckets.java | 4 +- .../otelstef/ExpHistogramBucketsEncoder.java | 15 +- .../example/otelstef/ExpHistogramValue.java | 11 +- .../otelstef/ExpHistogramValueEncoder.java | 17 +- .../example/otelstef/Float64ArrayEncoder.java | 6 +- .../com/example/otelstef/HistogramValue.java | 7 +- .../otelstef/HistogramValueEncoder.java | 15 +- .../com/example/otelstef/KeyValueList.java | 1 + .../example/otelstef/KeyValueListEncoder.java | 20 +- .../main/java/com/example/otelstef/Link.java | 8 +- .../example/otelstef/LinkArrayEncoder.java | 15 +- .../com/example/otelstef/LinkEncoder.java | 15 +- .../java/com/example/otelstef/LogRecord.java | 623 +++++++ .../example/otelstef/LogRecordDecoder.java | 324 ++++ .../example/otelstef/LogRecordEncoder.java | 407 +++++ .../main/java/com/example/otelstef/Logs.java | 301 ++++ .../com/example/otelstef/LogsDecoder.java | 207 +++ .../com/example/otelstef/LogsEncoder.java | 247 +++ .../java/com/example/otelstef/LogsReader.java | 108 ++ .../java/com/example/otelstef/LogsWriter.java | 142 ++ .../java/com/example/otelstef/Metric.java | 10 +- .../com/example/otelstef/MetricEncoder.java | 17 +- .../java/com/example/otelstef/Metrics.java | 8 +- .../com/example/otelstef/MetricsEncoder.java | 25 +- .../main/java/com/example/otelstef/Point.java | 6 +- .../com/example/otelstef/PointEncoder.java | 17 +- .../java/com/example/otelstef/PointValue.java | 30 +- .../example/otelstef/PointValueEncoder.java | 12 +- .../com/example/otelstef/QuantileValue.java | 4 +- .../otelstef/QuantileValueArrayEncoder.java | 15 +- .../otelstef/QuantileValueEncoder.java | 13 +- .../com/example/otelstef/ReaderState.java | 8 + .../java/com/example/otelstef/Resource.java | 5 +- .../com/example/otelstef/ResourceEncoder.java | 15 +- .../main/java/com/example/otelstef/Scope.java | 7 +- .../com/example/otelstef/ScopeEncoder.java | 15 +- .../com/example/otelstef/SeverityNumber.java | 33 + .../main/java/com/example/otelstef/Span.java | 16 +- .../com/example/otelstef/SpanEncoder.java | 21 +- .../java/com/example/otelstef/SpanStatus.java | 4 +- .../example/otelstef/SpanStatusEncoder.java | 13 +- .../main/java/com/example/otelstef/Spans.java | 6 +- .../com/example/otelstef/SpansEncoder.java | 21 +- .../com/example/otelstef/SummaryValue.java | 5 +- .../example/otelstef/SummaryValueEncoder.java | 15 +- .../example/otelstef/Uint64ArrayEncoder.java | 6 +- .../com/example/otelstef/WriterState.java | 10 + java/src/test/java/tests/CloneTest.java | 124 ++ .../src/test/java/tests/EncoderResetTest.java | 118 ++ stefc/templates/go/array.go.tmpl | 36 +- stefc/templates/go/modifiedfields.go.tmpl | 7 + stefc/templates/go/multimap.go.tmpl | 31 +- stefc/templates/go/oneof.go.tmpl | 80 +- stefc/templates/go/struct.go.tmpl | 116 +- stefc/templates/java/arrayEncoder.java.tmpl | 22 +- stefc/templates/java/multimap.java.tmpl | 1 + .../templates/java/multimapEncoder.java.tmpl | 22 +- stefc/templates/java/oneof.java.tmpl | 18 +- stefc/templates/java/oneofEncoder.java.tmpl | 8 +- stefc/templates/java/struct.java.tmpl | 5 +- stefc/templates/java/structEncoder.java.tmpl | 15 +- 175 files changed, 9551 insertions(+), 877 deletions(-) create mode 100644 benchmarks/encodings/log_encoding.go create mode 100644 benchmarks/encodings/otlp/logs.go rename benchmarks/encodings/otlp/{otlp.go => metrics.go} (50%) create mode 100644 benchmarks/encodings/stef/logs.go create mode 100644 benchmarks/testdata/astronomy-otellogs.zst create mode 100644 go/otel/otelstef/clone_test.go create mode 100644 go/otel/otelstef/logrecord.go create mode 100644 go/otel/otelstef/logs.go create mode 100644 go/otel/otelstef/logsreader.go create mode 100644 go/otel/otelstef/logswriter.go create mode 100644 go/otel/otelstef/logswriter_test.go create mode 100644 go/otel/otelstef/severitynumber.go create mode 100644 go/pdata/logs/otlp2stef.go create mode 100644 go/pdata/logs/otlp2stef_unsorted.go create mode 100644 go/pdata/logs/otlp2stef_unsorted_test.go create mode 100644 go/pdata/logs/sortedbyresource/converter.go create mode 100644 go/pdata/logs/sortedbyresource/converter_test.go create mode 100644 go/pdata/logs/sortedbyresource/sortedlogs.go create mode 100644 go/pdata/logs/stef2otlp.go create mode 100644 go/pdata/logs/stef2otlp_unsorted.go create mode 100644 go/pdata/logs/testtools/normalize.go create mode 100644 go/pdata/logs/testtools/normalize_test.go create mode 100644 java/src/main/java/com/example/otelstef/LogRecord.java create mode 100644 java/src/main/java/com/example/otelstef/LogRecordDecoder.java create mode 100644 java/src/main/java/com/example/otelstef/LogRecordEncoder.java create mode 100644 java/src/main/java/com/example/otelstef/Logs.java create mode 100644 java/src/main/java/com/example/otelstef/LogsDecoder.java create mode 100644 java/src/main/java/com/example/otelstef/LogsEncoder.java create mode 100644 java/src/main/java/com/example/otelstef/LogsReader.java create mode 100644 java/src/main/java/com/example/otelstef/LogsWriter.java create mode 100644 java/src/main/java/com/example/otelstef/SeverityNumber.java create mode 100644 java/src/test/java/tests/CloneTest.java create mode 100644 java/src/test/java/tests/EncoderResetTest.java diff --git a/benchmarks/benchmarks_test.go b/benchmarks/benchmarks_test.go index 4dcb2819..75c5a0ab 100644 --- a/benchmarks/benchmarks_test.go +++ b/benchmarks/benchmarks_test.go @@ -24,14 +24,19 @@ import ( "github.com/splunk/stef/go/pkg" ) -var speedEncodings = []encodings.MetricEncoding{ - &otlp.OTLPEncoding{}, +var metricsSpeedEncodings = []encodings.MetricEncoding{ + &otlp.MetricsEncoding{}, &stef.STEFEncoding{Opts: pkg.WriterOptions{Compression: pkg.CompressionNone}}, &stef.STEFUEncoding{Opts: pkg.WriterOptions{Compression: pkg.CompressionNone}}, &parquetenc.Encoding{}, &otelarrow.OtelArrowEncoding{}, } +var logsSpeedEncodings = []encodings.LogEncoding{ + &otlp.LogsEncoding{}, + &stef.LogsSTEFEncoding{Opts: pkg.WriterOptions{Compression: pkg.CompressionNone}}, +} + var benchmarkDataVariations = []struct { generator generators.Generator }{ @@ -49,6 +54,11 @@ var benchmarkDataVariations = []struct { var chart = BarOutput{} +type encodingNamer interface { + Name() string + LongName() string +} + func TestMain(m *testing.M) { // call flag.Parse() here if TestMain uses flags chart.Begin() @@ -58,9 +68,9 @@ func TestMain(m *testing.M) { func addZstdCompressTime( b *testing.B, - encoding encodings.MetricEncoding, + encoding encodingNamer, bodyBytes []byte, - dataPointCount int, + unitCount int, ) { if !chartsEnabled() { return @@ -76,11 +86,12 @@ func addZstdCompressTime( log.Fatal("compression failed") } } + value := float64(b.Elapsed().Nanoseconds()) / float64(b.N*unitCount) chart.RecordStacked( b, encoding.LongName(), "Zstd Compress", - float64(b.Elapsed().Nanoseconds())/float64(b.N*dataPointCount), + value, ) }, ) @@ -88,9 +99,9 @@ func addZstdCompressTime( func addZstdDecompressTime( b *testing.B, - encoding encodings.MetricEncoding, + encoding encodingNamer, bodyBytes []byte, - dataPointCount int, + unitCount int, ) { if !chartsEnabled() { return @@ -110,28 +121,28 @@ func addZstdDecompressTime( log.Fatal(err) } } + value := float64(b.Elapsed().Nanoseconds()) / float64(b.N*unitCount) chart.RecordStacked( b, encoding.LongName(), "Zstd Decompress", - float64(b.Elapsed().Nanoseconds())/float64(b.N*dataPointCount), + value, ) }, ) } -func BenchmarkSerializeNative(b *testing.B) { +func BenchmarkMetricsSerializeNative(b *testing.B) { chart.BeginSection("Speed Benchmarks") - chart.BeginChart("Serialization Speed", b) + chart.BeginChart("Metrics Serialize", "ns/point", b) defer chart.EndChart( - "ns/point", charts.WithColorsOpts(opts.Colors{"#92C5F9", "#12C5F9"}), ) for _, dataVariation := range benchmarkDataVariations { - for _, encoding := range speedEncodings { + for _, encoding := range metricsSpeedEncodings { if _, ok := encoding.(*otelarrow.OtelArrowEncoding); ok { // Skip Arrow, it does not have native serialization continue @@ -167,15 +178,55 @@ func BenchmarkSerializeNative(b *testing.B) { b.ReportAllocs() } -func BenchmarkDeserializeNative(b *testing.B) { - chart.BeginChart("Deserialization Speed", b) +func BenchmarkLogsSerializeNative(b *testing.B) { + chart.BeginChart("Logs Serialize", "ns/log", b) + defer chart.EndChart( + charts.WithColorsOpts(opts.Colors{"#92C5F9", "#12C5F9"}), + ) + + logsData, err := testutils.ReadOTLPLogsFile("testdata/astronomy-otellogs.zst") + require.NoError(b, err) + logCount := logsData.LogRecordCount() + require.Positive(b, logCount) + + for _, encoding := range logsSpeedEncodings { + inmem, err := encoding.FromOTLP(logsData) + require.NoError(b, err) + b.Run( + fmt.Sprintf("%s/serialize", encoding.Name()), + func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + bodyBytes, err := encoding.Encode(inmem) + if err != nil || bodyBytes == nil { + b.Fatal(err) + } + } + chart.RecordStacked( + b, + encoding.LongName(), + "Serialize", + float64(b.Elapsed().Nanoseconds())/float64(b.N*logCount), + ) + }, + ) + + bodyBytes, err := encoding.Encode(inmem) + require.NoError(b, err) + require.NotNil(b, bodyBytes) + addZstdCompressTime(b, encoding, bodyBytes, logCount) + } + b.ReportAllocs() +} + +func BenchmarkMetricsDeserializeNative(b *testing.B) { + chart.BeginChart("Metrics Deserialize", "ns/point", b) defer chart.EndChart( - "ns/point", charts.WithColorsOpts(opts.Colors{"#92C5F9", "#12C5F9"}), ) for _, dataVariation := range benchmarkDataVariations { - for _, encoding := range speedEncodings { + for _, encoding := range metricsSpeedEncodings { if _, ok := encoding.(*otelarrow.OtelArrowEncoding); ok { // Skip Arrow, it does not have native serialization continue @@ -224,15 +275,14 @@ func nsPerPoint(b *testing.B, dataPointCount int) float64 { return math.Round(float64(b.Elapsed().Nanoseconds()) / float64(totalDpCount)) } -func BenchmarkSerializeFromPdata(b *testing.B) { - chart.BeginChart("Serialization From pdata Speed", b) +func BenchmarkMetricsSerializeFromPdata(b *testing.B) { + chart.BeginChart("Metrics Serialize From pdata", "ns/point", b) defer chart.EndChart( - "ns/point", charts.WithColorsOpts(opts.Colors{"#92C5F9", "#12C5F9"}), ) for _, dataVariation := range benchmarkDataVariations { - for _, encoding := range speedEncodings { + for _, encoding := range metricsSpeedEncodings { if dataVariation.generator.GetName() == "hostandcollector-otelmetrics.zst" && encoding.Name() == "ARROW" { // Skip due to bug in Arrow encoding @@ -269,15 +319,56 @@ func BenchmarkSerializeFromPdata(b *testing.B) { b.ReportAllocs() } -func BenchmarkDeserializeToPdata(b *testing.B) { - chart.BeginChart("Deserialization To pdata Speed", b) +func BenchmarkLogsSerializeFromPdata(b *testing.B) { + chart.BeginChart("Logs Serialize From pdata", "ns/log", b) + defer chart.EndChart( + charts.WithColorsOpts(opts.Colors{"#92C5F9", "#12C5F9"}), + ) + + logsData, err := testutils.ReadOTLPLogsFile("testdata/astronomy-otellogs.zst") + require.NoError(b, err) + logCount := logsData.LogRecordCount() + require.Positive(b, logCount) + + for _, encoding := range logsSpeedEncodings { + b.Run( + fmt.Sprintf("%s/serialize", encoding.Name()), + func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + inmem, err := encoding.FromOTLP(logsData) + require.NoError(b, err) + bodyBytes, err := encoding.Encode(inmem) + require.NoError(b, err) + require.NotNil(b, bodyBytes) + } + chart.Record( + b, + encoding.LongName(), + "CPU time to serialize one log", + nsPerPoint(b, logCount), + ) + }, + ) + + inmem, err := encoding.FromOTLP(logsData) + require.NoError(b, err) + bodyBytes, err := encoding.Encode(inmem) + require.NoError(b, err) + require.NotNil(b, bodyBytes) + addZstdCompressTime(b, encoding, bodyBytes, logCount) + } + b.ReportAllocs() +} + +func BenchmarkMetricsDeserializeToPdata(b *testing.B) { + chart.BeginChart("Metrics Deserialize To pdata", "ns/point", b) defer chart.EndChart( - "ns/point", charts.WithColorsOpts(opts.Colors{"#92C5F9", "#12C5F9"}), ) for _, dataVariation := range benchmarkDataVariations { - for _, encoding := range speedEncodings { + for _, encoding := range metricsSpeedEncodings { if dataVariation.generator.GetName() == "hostandcollector-otelmetrics.zst" && encoding.Name() == "ARROW" { // Skip due to bug in Arrow encoding @@ -316,7 +407,48 @@ func BenchmarkDeserializeToPdata(b *testing.B) { b.ReportAllocs() } -func BenchmarkReaderReadMany(b *testing.B) { +func BenchmarkLogsDeserializeToPdata(b *testing.B) { + chart.BeginChart("Logs Deserialize To pdata", "ns/log", b) + defer chart.EndChart( + charts.WithColorsOpts(opts.Colors{"#92C5F9", "#12C5F9"}), + ) + + logsData, err := testutils.ReadOTLPLogsFile("testdata/astronomy-otellogs.zst") + require.NoError(b, err) + logCount := logsData.LogRecordCount() + require.Positive(b, logCount) + + for _, encoding := range logsSpeedEncodings { + inmem, err := encoding.FromOTLP(logsData) + require.NoError(b, err) + bodyBytes, err := encoding.Encode(inmem) + require.NoError(b, err) + require.NotNil(b, bodyBytes) + + b.Run( + fmt.Sprintf("%s/deserialize", encoding.Name()), + func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := encoding.ToOTLP(bodyBytes) + if err != nil { + b.Fatal(err) + } + } + chart.Record( + b, + encoding.LongName(), + "CPU time to deserialize one log", + nsPerPoint(b, logCount), + ) + }, + ) + addZstdDecompressTime(b, encoding, bodyBytes, logCount) + } + b.ReportAllocs() +} + +func BenchmarkMetricsReaderReadMany(b *testing.B) { generator := &generators.File{ FilePath: "testdata/hipstershop-otelmetrics.zst", } @@ -364,7 +496,7 @@ func BenchmarkReaderReadMany(b *testing.B) { ) } -func BenchmarkSTEFReaderRead(b *testing.B) { +func BenchmarkMetricsSTEFReaderRead(b *testing.B) { generator := &generators.File{ FilePath: "testdata/hipstershop-otelmetrics.zst", } @@ -405,9 +537,9 @@ func BenchmarkSTEFReaderRead(b *testing.B) { var multipartFiles = []string{"astronomy-otelmetrics"} -func BenchmarkSTEFSerializeMultipart(b *testing.B) { +func BenchmarkMetricsSTEFSerializeMultipart(b *testing.B) { for _, file := range multipartFiles { - parts, err := testutils.ReadMultipartOTLPFile("testdata/" + file + ".zst") + parts, err := testutils.ReadMultipartMetricsOTLPFile("testdata/" + file + ".zst") require.NoError(b, err) b.Run( file, @@ -442,9 +574,9 @@ func BenchmarkSTEFSerializeMultipart(b *testing.B) { } } -func BenchmarkSTEFDeserializeMultipart(b *testing.B) { +func BenchmarkMetricsSTEFDeserializeMultipart(b *testing.B) { for _, file := range multipartFiles { - parts, err := testutils.ReadMultipartOTLPFile("testdata/" + file + ".zst") + parts, err := testutils.ReadMultipartMetricsOTLPFile("testdata/" + file + ".zst") require.NoError(b, err) b.Run( file, diff --git a/benchmarks/charts.go b/benchmarks/charts.go index e06dde43..aa2a547f 100644 --- a/benchmarks/charts.go +++ b/benchmarks/charts.go @@ -16,6 +16,7 @@ import ( type BarOutput struct { t testing.TB title string + unit string file *os.File results map[string]map[string]float64 enabled bool @@ -25,13 +26,14 @@ func chartsEnabled() bool { return os.Getenv("UPDATE_BENCH_HTML") != "" } -func (c *BarOutput) BeginChart(title string, t testing.TB) { +func (c *BarOutput) BeginChart(title, unit string, t testing.TB) { c.t = t c.title = title + c.unit = unit c.results = map[string]map[string]float64{} } -func (c *BarOutput) EndChart(unit string, globalopts ...charts.GlobalOpts) { +func (c *BarOutput) EndChart(globalopts ...charts.GlobalOpts) { if !c.enabled { return } @@ -64,7 +66,7 @@ func (c *BarOutput) EndChart(unit string, globalopts ...charts.GlobalOpts) { charts.WithAnimation(false), charts.WithYAxisOpts( opts.YAxis{ - Name: unit, + Name: c.unit, }, ), ) @@ -117,14 +119,14 @@ func roundFloat(val float64, decimals int) float64 { func (c *BarOutput) Record(b *testing.B, encoding string, series string, val float64) { if b != nil { - b.ReportMetric(val, "ns/point") + b.ReportMetric(val, c.unit) } c.results[encoding] = map[string]float64{series: val} } func (c *BarOutput) RecordStacked(b *testing.B, encoding string, series string, val float64) { if b != nil { - b.ReportMetric(val, "ns/point") + b.ReportMetric(val, c.unit) } if c.results[encoding] == nil { c.results[encoding] = map[string]float64{} diff --git a/benchmarks/cmd/otlp2stef/main.go b/benchmarks/cmd/otlp2stef/main.go index f74a2e0a..235da7e8 100644 --- a/benchmarks/cmd/otlp2stef/main.go +++ b/benchmarks/cmd/otlp2stef/main.go @@ -35,7 +35,7 @@ func main() { } func convertOTLPtoSTEF(inputFilePath, outputFilePathNoExt, compressionMethod string) { - all, err := testutils.ReadOTLPFile(inputFilePath) + all, err := testutils.ReadOTLPMetricsFile(inputFilePath) if err != nil { log.Fatal(err) } diff --git a/benchmarks/correctness_test.go b/benchmarks/correctness_test.go index 04b86ebe..5abbde6a 100644 --- a/benchmarks/correctness_test.go +++ b/benchmarks/correctness_test.go @@ -8,17 +8,20 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" "github.com/splunk/stef/benchmarks/encodings/stef" "github.com/splunk/stef/benchmarks/testutils" "github.com/splunk/stef/go/otel/otelstef" + "github.com/splunk/stef/go/pdata/logs" + logtesttools "github.com/splunk/stef/go/pdata/logs/testtools" "github.com/splunk/stef/go/pdata/metrics" "github.com/splunk/stef/go/pdata/metrics/testtools" "github.com/splunk/stef/go/pkg" ) -func TestConvertSTEFFromToOTLP(t *testing.T) { +func TestMetricsConvertSTEFFromToOTLP(t *testing.T) { tests := []struct { file string }{ @@ -33,7 +36,7 @@ func TestConvertSTEFFromToOTLP(t *testing.T) { for _, test := range tests { t.Run( test.file, func(t *testing.T) { - otlpDataSrc, err := testutils.ReadOTLPFile(test.file) + otlpDataSrc, err := testutils.ReadOTLPMetricsFile(test.file) require.NoError(t, err) testtools.NormalizeMetrics(otlpDataSrc) @@ -69,6 +72,33 @@ func TestConvertSTEFFromToOTLP(t *testing.T) { } } +func TestLogsConvertSTEFFromToOTLP(t *testing.T) { + otlpDataSrc, err := testutils.ReadOTLPLogsFile("testdata/astronomy-otellogs.zst") + require.NoError(t, err) + + logtesttools.NormalizeLogs(otlpDataSrc) + srcCount := otlpDataSrc.LogRecordCount() + + buf := &pkg.MemChunkWriter{} + writer, err := otelstef.NewLogsWriter(buf, pkg.WriterOptions{}) + require.NoError(t, err) + + toStef := logs.OtlpToStefUnsorted{Sorted: true} + require.NoError(t, toStef.Convert(otlpDataSrc, writer)) + require.NoError(t, writer.Flush()) + + reader, err := otelstef.NewLogsReader(bytes.NewBuffer(buf.Bytes())) + require.NoError(t, err) + + toOtlp := logs.StefToOtlpUnsorted{} + otlpDataCopy, err := toOtlp.Convert(reader, true) + require.NoError(t, err) + + logtesttools.NormalizeLogs(otlpDataCopy) + assert.EqualValues(t, srcCount, otlpDataCopy.LogRecordCount()) + assert.True(t, bytes.Equal(logsToBytes(t, otlpDataSrc), logsToBytes(t, otlpDataCopy))) +} + func toBytes(t *testing.T, data pmetric.Metrics) []byte { marshaler := pmetric.ProtoMarshaler{} bytes, err := marshaler.MarshalMetrics(data) @@ -76,6 +106,13 @@ func toBytes(t *testing.T, data pmetric.Metrics) []byte { return bytes } +func logsToBytes(t *testing.T, data plog.Logs) []byte { + marshaler := plog.ProtoMarshaler{} + dataBytes, err := marshaler.MarshalLogs(data) + require.NoError(t, err) + return dataBytes +} + func TestTEFMultiPart(t *testing.T) { var testInputOtlpFiles = []string{ "testdata/astronomy-otelmetrics.zst", @@ -88,7 +125,7 @@ func TestTEFMultiPart(t *testing.T) { t.Run( inputFile, func(t *testing.T) { - parts, err := testutils.ReadMultipartOTLPFile(inputFile) + parts, err := testutils.ReadMultipartMetricsOTLPFile(inputFile) require.NoError(t, err) tefStream, err := tefEncoding.StartMultipart("") diff --git a/benchmarks/encodings/log_encoding.go b/benchmarks/encodings/log_encoding.go new file mode 100644 index 00000000..5c22c397 --- /dev/null +++ b/benchmarks/encodings/log_encoding.go @@ -0,0 +1,12 @@ +package encodings + +import "go.opentelemetry.io/collector/pdata/plog" + +type LogEncoding interface { + Name() string + LongName() string + FromOTLP(batch plog.Logs) (InMemoryData, error) + Encode(data InMemoryData) ([]byte, error) + Decode([]byte) (any, error) + ToOTLP(data []byte) (plog.Logs, error) +} diff --git a/benchmarks/encodings/otlp/logs.go b/benchmarks/encodings/otlp/logs.go new file mode 100644 index 00000000..92605b13 --- /dev/null +++ b/benchmarks/encodings/otlp/logs.go @@ -0,0 +1,37 @@ +package otlp + +import ( + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/splunk/stef/benchmarks/encodings" +) + +type LogsEncoding struct{} + +var _ encodings.LogEncoding = (*LogsEncoding)(nil) + +func (*LogsEncoding) FromOTLP(data plog.Logs) (encodings.InMemoryData, error) { + return data, nil +} + +func (*LogsEncoding) Encode(data encodings.InMemoryData) ([]byte, error) { + marshaler := plog.ProtoMarshaler{} + return marshaler.MarshalLogs(data.(plog.Logs)) +} + +func (d *LogsEncoding) Decode(data []byte) (any, error) { + return d.ToOTLP(data) +} + +func (*LogsEncoding) ToOTLP(data []byte) (plog.Logs, error) { + unmarshaler := plog.ProtoUnmarshaler{} + return unmarshaler.UnmarshalLogs(data) +} + +func (*LogsEncoding) Name() string { + return "OTLP" +} + +func (*LogsEncoding) LongName() string { + return "Protobuf OTLP" +} diff --git a/benchmarks/encodings/otlp/otlp.go b/benchmarks/encodings/otlp/metrics.go similarity index 50% rename from benchmarks/encodings/otlp/otlp.go rename to benchmarks/encodings/otlp/metrics.go index 4ab2200d..9288906a 100644 --- a/benchmarks/encodings/otlp/otlp.go +++ b/benchmarks/encodings/otlp/metrics.go @@ -7,40 +7,40 @@ import ( "github.com/splunk/stef/benchmarks/testutils" ) -type OTLPEncoding struct { +type MetricsEncoding struct { } -func (d *OTLPEncoding) FromOTLP(data pmetric.Metrics) (encodings.InMemoryData, error) { +func (d *MetricsEncoding) FromOTLP(data pmetric.Metrics) (encodings.InMemoryData, error) { return data, nil } -func (d *OTLPEncoding) Encode(data encodings.InMemoryData) ([]byte, error) { +func (d *MetricsEncoding) Encode(data encodings.InMemoryData) ([]byte, error) { marshaler := pmetric.ProtoMarshaler{} return marshaler.MarshalMetrics(data.(pmetric.Metrics)) } -func (d *OTLPEncoding) Decode(b []byte) (any, error) { +func (d *MetricsEncoding) Decode(b []byte) (any, error) { return d.ToOTLP(b) } -func (*OTLPEncoding) ToOTLP(data []byte) (pmetric.Metrics, error) { +func (*MetricsEncoding) ToOTLP(data []byte) (pmetric.Metrics, error) { marshaler := pmetric.ProtoUnmarshaler{} return marshaler.UnmarshalMetrics(data) } -func (*OTLPEncoding) Name() string { +func (*MetricsEncoding) Name() string { return "OTLP" } -func (*OTLPEncoding) LongName() string { +func (*MetricsEncoding) LongName() string { return "Protobuf OTLP" } -type otlpMultipart struct { +type metricsMultipart struct { compression string bytes []byte } -func (o *otlpMultipart) AppendPart(part pmetric.Metrics) error { +func (o *metricsMultipart) AppendPart(part pmetric.Metrics) error { marshaler := pmetric.ProtoMarshaler{} b, err := marshaler.MarshalMetrics(part) if err != nil { @@ -55,10 +55,10 @@ func (o *otlpMultipart) AppendPart(part pmetric.Metrics) error { return nil } -func (o *otlpMultipart) FinishStream() ([]byte, error) { +func (o *metricsMultipart) FinishStream() ([]byte, error) { return o.bytes, nil } -func (d *OTLPEncoding) StartMultipart(compression string) (encodings.MetricMultipartStream, error) { - return &otlpMultipart{compression: compression}, nil +func (d *MetricsEncoding) StartMultipart(compression string) (encodings.MetricMultipartStream, error) { + return &metricsMultipart{compression: compression}, nil } diff --git a/benchmarks/encodings/stef/logs.go b/benchmarks/encodings/stef/logs.go new file mode 100644 index 00000000..fd3701e5 --- /dev/null +++ b/benchmarks/encodings/stef/logs.go @@ -0,0 +1,77 @@ +package stef + +import ( + "bytes" + "io" + + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/splunk/stef/benchmarks/encodings" + "github.com/splunk/stef/go/otel/otelstef" + logconvert "github.com/splunk/stef/go/pdata/logs" + "github.com/splunk/stef/go/pdata/logs/sortedbyresource" + "github.com/splunk/stef/go/pkg" +) + +// LogsSTEFEncoding is resource-sorted STEF format for logs. +type LogsSTEFEncoding struct { + Opts pkg.WriterOptions +} + +var _ encodings.LogEncoding = (*LogsSTEFEncoding)(nil) + +func (*LogsSTEFEncoding) FromOTLP(data plog.Logs) (encodings.InMemoryData, error) { + return sortedbyresource.OtlpToSortedTree(data), nil +} + +func (d *LogsSTEFEncoding) Encode(data encodings.InMemoryData) ([]byte, error) { + tree := data.(*sortedbyresource.SortedTree) + output := &pkg.MemChunkWriter{} + writer, err := otelstef.NewLogsWriter(output, d.Opts) + if err != nil { + return nil, err + } + if err := tree.ToStef(writer); err != nil { + return nil, err + } + if err := writer.Flush(); err != nil { + return nil, err + } + return output.Bytes(), nil +} + +func (*LogsSTEFEncoding) Decode(data []byte) (any, error) { + reader, err := otelstef.NewLogsReader(bytes.NewReader(data)) + if err != nil { + return nil, err + } + for { + if err := reader.Read(pkg.ReadOptions{}); err != nil { + if err == io.EOF { + return nil, nil + } + return nil, err + } + } +} + +func (*LogsSTEFEncoding) ToOTLP(data []byte) (plog.Logs, error) { + reader, err := otelstef.NewLogsReader(bytes.NewReader(data)) + if err != nil { + return plog.NewLogs(), err + } + converter := logconvert.StefToOtlpUnsorted{} + return converter.Convert(reader, true) +} + +func (e *LogsSTEFEncoding) Name() string { + name := "STEF" + if e.Opts.Compression != pkg.CompressionNone { + name += "Z" + } + return name +} + +func (*LogsSTEFEncoding) LongName() string { + return "STEF" +} diff --git a/benchmarks/generators/file.go b/benchmarks/generators/file.go index 00869d57..3456c66b 100644 --- a/benchmarks/generators/file.go +++ b/benchmarks/generators/file.go @@ -23,7 +23,7 @@ func (f *File) GetName() string { } func (f *File) Generate() pmetric.Metrics { - all, err := testutils.ReadOTLPFile(f.FilePath) + all, err := testutils.ReadOTLPMetricsFile(f.FilePath) if err != nil { log.Fatalf("Error reading file %s: %v", f.FilePath, err) } diff --git a/benchmarks/otlp2sortedtree_test.go b/benchmarks/otlp2sortedtree_test.go index 4a52ae7b..00c711a4 100644 --- a/benchmarks/otlp2sortedtree_test.go +++ b/benchmarks/otlp2sortedtree_test.go @@ -36,7 +36,7 @@ func mapContains[K any](searchFor, inTree *b.Tree[K, bool]) bool { } func TestConvertFromOTLP(t *testing.T) { - otlpData, err := testutils.ReadOTLPFile("testdata/hipstershop-otelmetrics.zst") + otlpData, err := testutils.ReadOTLPMetricsFile("testdata/hipstershop-otelmetrics.zst") require.NoError(t, err) sorted, err := sortedbymetric.OtlpToSortedTree(otlpData) diff --git a/benchmarks/readwrite_test.go b/benchmarks/readwrite_test.go index b3bc507e..fe0c9629 100644 --- a/benchmarks/readwrite_test.go +++ b/benchmarks/readwrite_test.go @@ -76,7 +76,7 @@ func TestCopy(t *testing.T) { } } -func BenchmarkReadSTEF(b *testing.B) { +func BenchmarkMetricsReadSTEF(b *testing.B) { tefBytes, err := os.ReadFile("testdata/generated/hipstershop-otelmetrics.stefz") require.NoError(b, err) @@ -130,7 +130,7 @@ func BenchmarkReadSTEF(b *testing.B) { b.ReportMetric(float64(b.Elapsed().Nanoseconds())/float64(b.N*recCount), "ns/point") } -func BenchmarkReadSTEFZ(b *testing.B) { +func BenchmarkMetricsReadSTEFZ(b *testing.B) { tefBytes, err := os.ReadFile("testdata/generated/hipstershop-otelmetrics.stefz") require.NoError(b, err) @@ -157,7 +157,7 @@ func BenchmarkReadSTEFZ(b *testing.B) { b.ReportMetric(float64(b.Elapsed().Nanoseconds())/float64(b.N*recCount), "ns/point") } -func BenchmarkReadSTEFZWriteSTEF(b *testing.B) { +func BenchmarkMetricsReadSTEFZWriteSTEF(b *testing.B) { tefBytes, err := os.ReadFile("testdata/generated/hipstershop-otelmetrics.stefz") require.NoError(b, err) diff --git a/benchmarks/size_test.go b/benchmarks/size_test.go index c878527b..e2562a49 100644 --- a/benchmarks/size_test.go +++ b/benchmarks/size_test.go @@ -12,6 +12,7 @@ import ( "github.com/go-echarts/go-echarts/v2/charts" "github.com/go-echarts/go-echarts/v2/opts" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/ptrace" "github.com/splunk/stef/benchmarks/encodings" @@ -22,6 +23,7 @@ import ( "github.com/splunk/stef/benchmarks/generators" "github.com/splunk/stef/benchmarks/testutils" "github.com/splunk/stef/go/otel/otelstef" + pdatalogs "github.com/splunk/stef/go/pdata/logs" "github.com/splunk/stef/go/pdata/traces" "github.com/splunk/stef/go/pkg" ) @@ -69,7 +71,7 @@ var metricsDataVariations = []struct { } var sizeEncodings = []encodings.MetricEncoding{ - &otlp.OTLPEncoding{}, + &otlp.MetricsEncoding{}, &stef.STEFEncoding{Opts: pkg.WriterOptions{Compression: pkg.CompressionNone}}, &stef.STEFUEncoding{Opts: pkg.WriterOptions{Compression: pkg.CompressionNone}}, &parquetenc.Encoding{}, @@ -98,7 +100,7 @@ func TestMetricsSize(t *testing.T) { wantChart := strings.HasSuffix(dataVariation.generator.GetName(), ".zst") if wantChart { - chart.BeginChart("Dataset: "+dataVariation.generator.GetName(), t) + chart.BeginChart("Dataset: "+dataVariation.generator.GetName(), "Bytes/point", t) } for _, encoding := range sizeEncodings { @@ -179,7 +181,6 @@ func TestMetricsSize(t *testing.T) { if wantChart { chart.EndChart( - "Bytes/point", charts.WithColorsOpts(opts.Colors{"#92C5F9"}), ) } @@ -201,7 +202,7 @@ func TestMetricsMultipart(t *testing.T) { } testMultipartEncodings := []encodings.MetricMultipartEncoding{ - &otlp.OTLPEncoding{}, + &otlp.MetricsEncoding{}, &stef.STEFEncoding{}, &stef.STEFUEncoding{}, &otelarrow.OtelArrowEncoding{}, @@ -213,7 +214,7 @@ func TestMetricsMultipart(t *testing.T) { for _, compression := range compressions { for _, dataset := range datasets { - chart.BeginChart("Dataset: "+dataset.name, t) + chart.BeginChart("Dataset: "+dataset.name, "Bytes/point", t) fmt.Printf("%-30s %4v %9v %4v\n", dataset.name, "Comp", "Bytes", "Ratio") @@ -225,7 +226,7 @@ func TestMetricsMultipart(t *testing.T) { // Encode each part one after another and write to the same STEF stream. // This models more closely the operation of STEF exporter in Collector. - parts, err := testutils.ReadMultipartOTLPFile("testdata/" + dataset.name + ".zst") + parts, err := testutils.ReadMultipartMetricsOTLPFile("testdata/" + dataset.name + ".zst") require.NoError(t, err) pointCount := 0 @@ -258,7 +259,6 @@ func TestMetricsMultipart(t *testing.T) { } chart.EndChart( - "Bytes/point", charts.WithColorsOpts(opts.Colors{"#87BB62"}), ) } @@ -266,7 +266,7 @@ func TestMetricsMultipart(t *testing.T) { } func TestSTEFVeryShortFrames(t *testing.T) { - input, err := testutils.ReadOTLPFile("testdata/hipstershop-otelmetrics.zst") + input, err := testutils.ReadOTLPMetricsFile("testdata/hipstershop-otelmetrics.zst") require.NoError(t, err) compressions := []pkg.Compression{pkg.CompressionNone, pkg.CompressionZstd} @@ -348,7 +348,7 @@ func TestTracesMultipart(t *testing.T) { } fmt.Println(compressionStr) - chart.BeginChart("Dataset: "+fileName, t) + chart.BeginChart("Dataset: "+fileName, "Bytes/span", t) otlpSize := 0 spanCount := 0 @@ -382,8 +382,11 @@ func TestTracesMultipart(t *testing.T) { converter := traces.OtlpToStefUnsorted{Sorted: sorted} - for i := 0; i < len(traceData); i++ { - err = converter.Convert(traceData[i].(ptrace.Traces), writer) + for i := 0; i < len(otlpParts); i++ { + tracesPart, err := u.UnmarshalTraces(otlpParts[i]) + require.NoError(t, err) + + err = converter.Convert(tracesPart, writer) require.NoError(t, err) err = writer.Flush() @@ -412,7 +415,110 @@ func TestTracesMultipart(t *testing.T) { ) } chart.EndChart( - "Bytes/span", + charts.WithColorsOpts(opts.Colors{"#87BB62"}), + ) + } + } +} + +func TestLogsMultipart(t *testing.T) { + u := plog.ProtoUnmarshaler{} + + fileNames := []string{"astronomy-otellogs"} + + chart.BeginSection("Size - Many Batches, Multipart Logs") + + for _, fileName := range fileNames { + fmt.Println("======= " + fileName) + + var otlpParts [][]byte + logsData, err := testutils.ReadMultipartOTLPFileGeneric( + "testdata/"+fileName+".zst", func(data []byte) (any, error) { + otlpParts = append(otlpParts, data) + return u.UnmarshalLogs(data) + }, + ) + require.NoError(t, err) + + compressions := []pkg.Compression{pkg.CompressionNone, pkg.CompressionZstd} + sorteds := []bool{false, true} + + for _, compression := range compressions { + var compressionStr string + if compression == pkg.CompressionZstd { + compressionStr = "zstd" + } else { + compressionStr = "none" + } + fmt.Println(compressionStr) + + chart.BeginChart("Dataset: "+fileName, "Bytes/log", t) + + otlpSize := 0 + logCount := 0 + for i := 0; i < len(logsData); i++ { + if compression == pkg.CompressionNone { + otlpSize += len(otlpParts[i]) + } else { + otlpZstd := testutils.CompressZstd(otlpParts[i]) + otlpSize += len(otlpZstd) + } + logCount += logsData[i].(plog.Logs).LogRecordCount() + } + + chart.Record( + nil, "OTLP", "Bytes/log, compression="+compressionStr, + unitSize(otlpSize, logCount), + ) + + for _, sorted := range sorteds { + var sortedStr string + if sorted { + sortedStr = "Sorted" + } else { + sortedStr = "Unsorted" + } + fmt.Println(sortedStr) + + outputBuf := &pkg.MemChunkWriter{} + writer, err := otelstef.NewLogsWriter(outputBuf, pkg.WriterOptions{Compression: compression}) + require.NoError(t, err) + + converter := pdatalogs.OtlpToStefUnsorted{Sorted: sorted} + + for i := 0; i < len(otlpParts); i++ { + logsPart, err := u.UnmarshalLogs(otlpParts[i]) + require.NoError(t, err) + + err = converter.Convert(logsPart, writer) + require.NoError(t, err) + + err = writer.Flush() + require.NoError(t, err) + } + + stefSize := len(outputBuf.Bytes()) + + fmt.Printf( + "Logs OTLP: %8d (%5.1f bytes/log)\n", + otlpSize, + float64(otlpSize)/float64(logCount), + ) + fmt.Printf( + "Logs STEF: %8d (%5.1f bytes/log)\n", + stefSize, + float64(stefSize)/float64(logCount), + ) + fmt.Printf( + "Ratio: %8.2f\n", float64(otlpSize)/float64(stefSize), + ) + + chart.Record( + nil, "STEF "+sortedStr, "Bytes/log, compression="+compressionStr, + unitSize(stefSize, logCount), + ) + } + chart.EndChart( charts.WithColorsOpts(opts.Colors{"#87BB62"}), ) } diff --git a/benchmarks/testdata/astronomy-otellogs.zst b/benchmarks/testdata/astronomy-otellogs.zst new file mode 100644 index 0000000000000000000000000000000000000000..198730ccf71d5a77f3bc8747117bcc6233680dd7 GIT binary patch literal 499276 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 z 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 z

    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

    Size Benchmarks - One Large Batch

    -
    +
    - +
    -
    +
    - +
    -
    +
    - +

    Size - Many Batches, Multipart Metrics

    -
    +
    - +
    -
    +
    - +
    -
    +
    - +
    -
    +
    - +

    Size - Many Batches, Multipart Traces

    -
    +
    +
    + + + +
    +
    +
    + + + +
    +
    - +
    -
    +
    - + +

    Size - Many Batches, Multipart Logs

    -
    +
    - +
    -
    +
    - +

    Speed Benchmarks

    -
    +
    - +
    -
    +
    - + + +
    +
    +
    + +
    -
    +
    - +
    -
    +
    - + + +
    +
    +
    + + + +
    +
    +
    + + - \ No newline at end of file + diff --git a/examples/ints/internal/ints/modifiedfields.go b/examples/ints/internal/ints/modifiedfields.go index 2f02cad8..6d5b84ba 100644 --- a/examples/ints/internal/ints/modifiedfields.go +++ b/examples/ints/internal/ints/modifiedfields.go @@ -115,6 +115,13 @@ func (m *modifiedFieldsMultimap) fixParent(parentModifiedFields *modifiedFields) m.vals.parent = parentModifiedFields } +func (m *modifiedFieldsMultimap) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.keys.parent = parentModifiedFields + m.vals.parent = parentModifiedFields + m.keys.parentBit = parentModifiedBit + m.vals.parentBit = parentModifiedBit +} + // Returns a bitmask to be set in keys or vals to mark modification of // multimap element at index. func (m *modifiedFieldsMultimap) maskForIndex(index int) uint64 { diff --git a/examples/ints/internal/ints/record.go b/examples/ints/internal/ints/record.go index cf7ef42e..1f071f36 100644 --- a/examples/ints/internal/ints/record.go +++ b/examples/ints/internal/ints/record.go @@ -77,6 +77,12 @@ func (s *Record) fixParent(parentModifiedFields *modifiedFields) { s.modifiedFields.parent = parentModifiedFields } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Record) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit +} + func (s *Record) freeze() { s.modifiedFields.freeze() } @@ -118,6 +124,12 @@ func (s *Record) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Record) clearModifiedRecursively() { + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Record) computeDiff(val *Record) (ret bool) { @@ -134,17 +146,14 @@ func (s *Record) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Record) cloneShared(allocators *Allocators) Record { - return s.Clone(allocators) -} - -func (s *Record) Clone(allocators *Allocators) Record { - c := Record{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Record) CloneTo(dst *Record, allocators *Allocators) { + *dst = Record{ uint64: s.uint64, } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/examples/jsonl/internal/jsonstef/jsonobject.go b/examples/jsonl/internal/jsonstef/jsonobject.go index 06cde78a..3b5c6891 100644 --- a/examples/jsonl/internal/jsonstef/jsonobject.go +++ b/examples/jsonl/internal/jsonstef/jsonobject.go @@ -60,6 +60,15 @@ func (m *JsonObject) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointers and bits in a newly copied multimap. +func (m *JsonObject) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.modifiedElems.attachParent(parentModifiedFields, parentModifiedBit) + elems := m.elems[:m.initedCount] + for i := 0; i < len(elems); i++ { + elems[i].value.attachParent(&m.modifiedElems.vals, m.modifiedElems.maskForIndex(i)) + } +} + func (m *JsonObject) canBeShared() bool { // Multimap can never be shared. return false @@ -122,6 +131,14 @@ func (m *JsonObject) setUnmodifiedRecursively() { m.modifiedElems.setUnmodifiedAll() } +// clearModifiedRecursively clears modification state in all mutable elements. +func (m *JsonObject) clearModifiedRecursively() { + for i := 0; i < len(m.elems); i++ { + m.elems[i].value.clearModifiedRecursively() + } + m.modifiedElems.setUnmodifiedAll() +} + // computeDiff compares m to val and returns true if they differ. // All fields that are different in m will be marked as modified. func (m *JsonObject) computeDiff(val *JsonObject) (ret bool) { diff --git a/examples/jsonl/internal/jsonstef/jsonvalue.go b/examples/jsonl/internal/jsonstef/jsonvalue.go index c7d39ae9..5b510fd9 100644 --- a/examples/jsonl/internal/jsonstef/jsonvalue.go +++ b/examples/jsonl/internal/jsonstef/jsonvalue.go @@ -82,6 +82,19 @@ func (s *JsonValue) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointer and bit in a newly copied oneof. +func (s *JsonValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.parentModifiedFields = parentModifiedFields + s.parentModifiedBit = parentModifiedBit + + switch s.Type() { + case JsonValueTypeObject: + s.objectPtr().attachParent(parentModifiedFields, parentModifiedBit) + case JsonValueTypeArray: + s.arrayPtr().attachParent(parentModifiedFields, parentModifiedBit) + } +} + type JsonValueType byte const ( @@ -244,32 +257,41 @@ func (s *JsonValue) canBeShared() bool { return false } +func (s *JsonValue) clone(allocators *Allocators) *JsonValue { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(JsonValue{}))) + c := allocators.JsonValue.Alloc() + s.CloneTo(c, allocators) + return c +} + func (s *JsonValue) cloneShared(allocators *Allocators) *JsonValue { - // Oneof is not shareable, so cloneShared is just a Clone. - return s.Clone(allocators) + // Oneof is not shareable, so cloneShared is just a clone. + return s.clone(allocators) } -func (s *JsonValue) Clone(allocators *Allocators) *JsonValue { - allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(JsonValue{}))) - c := allocators.JsonValue.Alloc() - c.clearValSetType(s.Type()) +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *JsonValue) CloneTo(dst *JsonValue, allocators *Allocators) { + *dst = JsonValue{} + dst.clearValSetType(s.Type()) switch s.Type() { case JsonValueTypeObject: allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(JsonObject{}))) - c.allocObjectAlloc(allocators) - copyToNewJsonObject(c.objectPtr(), s.objectPtr(), allocators) + dst.allocObjectAlloc(allocators) + copyToNewJsonObject(dst.objectPtr(), s.objectPtr(), allocators) case JsonValueTypeArray: allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(JsonValueArray{}))) - c.allocArrayAlloc(allocators) - copyToNewJsonValueArray(c.arrayPtr(), s.arrayPtr(), allocators) + dst.allocArrayAlloc(allocators) + copyToNewJsonValueArray(dst.arrayPtr(), s.arrayPtr(), allocators) case JsonValueTypeString: - c.setString(s.String()) + dst.setString(s.String()) case JsonValueTypeNumber: - *c.numberPtr() = *s.numberPtr() + *dst.numberPtr() = *s.numberPtr() case JsonValueTypeBool: - *c.boolPtr() = *s.boolPtr() + *dst.boolPtr() = *s.boolPtr() } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -371,6 +393,16 @@ func (s *JsonValue) setUnmodifiedRecursively() { } } +// clearModifiedRecursively clears modification state in all mutable descendants. +func (s *JsonValue) clearModifiedRecursively() { + switch s.Type() { + case JsonValueTypeObject: + s.objectPtr().clearModifiedRecursively() + case JsonValueTypeArray: + s.arrayPtr().clearModifiedRecursively() + } +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *JsonValue) computeDiff(val *JsonValue) (ret bool) { diff --git a/examples/jsonl/internal/jsonstef/jsonvaluearray.go b/examples/jsonl/internal/jsonstef/jsonvaluearray.go index 05e017f2..0607eaf0 100644 --- a/examples/jsonl/internal/jsonstef/jsonvaluearray.go +++ b/examples/jsonl/internal/jsonstef/jsonvaluearray.go @@ -52,6 +52,15 @@ func (e *JsonValueArray) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *JsonValueArray) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit + for i := range e.elems { + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + } +} + func (e *JsonValueArray) canBeShared() bool { // An array can never be shared. return false @@ -96,6 +105,14 @@ func (e *JsonValueArray) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *JsonValueArray) clearModifiedRecursively() { + for i := 0; i < len(e.elems); i++ { + e.elems[i].clearModifiedRecursively() + } + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *JsonValueArray) computeDiff(val *JsonValueArray) (ret bool) { diff --git a/examples/jsonl/internal/jsonstef/modifiedfields.go b/examples/jsonl/internal/jsonstef/modifiedfields.go index d7d07bd7..b39bec1d 100644 --- a/examples/jsonl/internal/jsonstef/modifiedfields.go +++ b/examples/jsonl/internal/jsonstef/modifiedfields.go @@ -115,6 +115,13 @@ func (m *modifiedFieldsMultimap) fixParent(parentModifiedFields *modifiedFields) m.vals.parent = parentModifiedFields } +func (m *modifiedFieldsMultimap) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.keys.parent = parentModifiedFields + m.vals.parent = parentModifiedFields + m.keys.parentBit = parentModifiedBit + m.vals.parentBit = parentModifiedBit +} + // Returns a bitmask to be set in keys or vals to mark modification of // multimap element at index. func (m *modifiedFieldsMultimap) maskForIndex(index int) uint64 { diff --git a/examples/jsonl/internal/jsonstef/record.go b/examples/jsonl/internal/jsonstef/record.go index b233bb83..bc4d81e8 100644 --- a/examples/jsonl/internal/jsonstef/record.go +++ b/examples/jsonl/internal/jsonstef/record.go @@ -85,6 +85,13 @@ func (s *Record) fixParent(parentModifiedFields *modifiedFields) { s.value.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Record) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.value.attachParent(&s.modifiedFields, fieldModifiedRecordValue) +} + func (s *Record) freeze() { if s.isFrozen() { return @@ -126,6 +133,13 @@ func (s *Record) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Record) clearModifiedRecursively() { + s.value.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Record) computeDiff(val *Record) (ret bool) { @@ -142,17 +156,13 @@ func (s *Record) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Record) cloneShared(allocators *Allocators) Record { - return s.Clone(allocators) -} - -func (s *Record) Clone(allocators *Allocators) Record { - c := Record{ - value: s.value.cloneShared(allocators), - } - return c +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Record) CloneTo(dst *Record, allocators *Allocators) { + *dst = Record{} + dst.value = s.value.cloneShared(allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/examples/profile/internal/profile/function.go b/examples/profile/internal/profile/function.go index fc001788..d7d22ada 100644 --- a/examples/profile/internal/profile/function.go +++ b/examples/profile/internal/profile/function.go @@ -98,6 +98,12 @@ func (s *Function) fixParent(parentModifiedFields *modifiedFields) { s.modifiedFields.parent = parentModifiedFields } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Function) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit +} + // Freeze the struct. Any attempt to modify it after this will panic. // This marks the struct as eligible for safely sharing by pointer without cloning, // which can improve encoding performance. @@ -221,6 +227,12 @@ func (s *Function) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Function) clearModifiedRecursively() { + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Function) computeDiff(val *Function) (ret bool) { @@ -252,6 +264,14 @@ func (s *Function) canBeShared() bool { return s.isFrozen() } +// clone returns a clone of s allocated using allocators. +func (s *Function) clone(allocators *Allocators) *Function { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Function{}))) + c := allocators.Function.Alloc() + s.CloneTo(c, allocators) + return c +} + // cloneShared returns a clone of s. It may return s if it is safe to share without cloning // (for example if s is frozen). func (s *Function) cloneShared(allocators *Allocators) *Function { @@ -259,19 +279,20 @@ func (s *Function) cloneShared(allocators *Allocators) *Function { // If s is frozen it means it is safe to share without cloning. return s } - return s.Clone(allocators) + return s.clone(allocators) } -func (s *Function) Clone(allocators *Allocators) *Function { - allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Function{}))) - c := allocators.Function.Alloc() - *c = Function{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Function) CloneTo(dst *Function, allocators *Allocators) { + *dst = Function{ name: s.name, systemName: s.systemName, filename: s.filename, startLine: s.startLine, } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -461,7 +482,7 @@ func (d *FunctionEncoderDict) Add(val *Function) { d.dict.Set(val, refNum) return } - clone := val.Clone(d.allocators) // Clone before adding to dictionary. + clone := val.clone(d.allocators) // Clone before adding to dictionary. clone.Freeze() // Freeze the clone so that it can be safely shared by pointer. d.dict.Set(clone, refNum) } @@ -795,7 +816,7 @@ func (d *FunctionDecoder) Decode(dstPtr **Function) error { // *dstPtr is pointing to a element in the dictionary. We are not allowed // to modify it. Make a clone of it and decode into the clone. - val := (*dstPtr).Clone(d.allocators) + val := (*dstPtr).clone(d.allocators) if d.allocators.allocSizeChecker.IsOverLimit() { return pkg.ErrRecordAllocLimitExceeded } diff --git a/examples/profile/internal/profile/labels.go b/examples/profile/internal/profile/labels.go index b7dddb25..5570a06c 100644 --- a/examples/profile/internal/profile/labels.go +++ b/examples/profile/internal/profile/labels.go @@ -60,6 +60,15 @@ func (m *Labels) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointers and bits in a newly copied multimap. +func (m *Labels) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.modifiedElems.attachParent(parentModifiedFields, parentModifiedBit) + elems := m.elems[:m.initedCount] + for i := 0; i < len(elems); i++ { + elems[i].value.attachParent(&m.modifiedElems.vals, m.modifiedElems.maskForIndex(i)) + } +} + func (m *Labels) canBeShared() bool { // Multimap can never be shared. return false @@ -122,6 +131,14 @@ func (m *Labels) setUnmodifiedRecursively() { m.modifiedElems.setUnmodifiedAll() } +// clearModifiedRecursively clears modification state in all mutable elements. +func (m *Labels) clearModifiedRecursively() { + for i := 0; i < len(m.elems); i++ { + m.elems[i].value.clearModifiedRecursively() + } + m.modifiedElems.setUnmodifiedAll() +} + // computeDiff compares m to val and returns true if they differ. // All fields that are different in m will be marked as modified. func (m *Labels) computeDiff(val *Labels) (ret bool) { diff --git a/examples/profile/internal/profile/labelvalue.go b/examples/profile/internal/profile/labelvalue.go index 4ad0c6e9..e9ae19dc 100644 --- a/examples/profile/internal/profile/labelvalue.go +++ b/examples/profile/internal/profile/labelvalue.go @@ -76,6 +76,17 @@ func (s *LabelValue) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointer and bit in a newly copied oneof. +func (s *LabelValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.parentModifiedFields = parentModifiedFields + s.parentModifiedBit = parentModifiedBit + + switch s.Type() { + case LabelValueTypeNum: + s.numPtr().attachParent(parentModifiedFields, parentModifiedBit) + } +} + type LabelValueType byte const ( @@ -169,23 +180,21 @@ func (s *LabelValue) canBeShared() bool { return false } -func (s *LabelValue) cloneShared(allocators *Allocators) LabelValue { - // Oneof is not shareable, so cloneShared is just a Clone. - return s.Clone(allocators) -} - -func (s *LabelValue) Clone(allocators *Allocators) LabelValue { - c := LabelValue{} - c.clearValSetType(s.Type()) +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *LabelValue) CloneTo(dst *LabelValue, allocators *Allocators) { + *dst = LabelValue{} + dst.clearValSetType(s.Type()) switch s.Type() { case LabelValueTypeStr: - c.setStr(s.Str()) + dst.setStr(s.Str()) case LabelValueTypeNum: allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(NumValue{}))) - c.allocNumAlloc(allocators) - copyToNewNumValue(c.numPtr(), s.numPtr(), allocators) + dst.allocNumAlloc(allocators) + copyToNewNumValue(dst.numPtr(), s.numPtr(), allocators) } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -260,6 +269,14 @@ func (s *LabelValue) setUnmodifiedRecursively() { } } +// clearModifiedRecursively clears modification state in all mutable descendants. +func (s *LabelValue) clearModifiedRecursively() { + switch s.Type() { + case LabelValueTypeNum: + s.numPtr().clearModifiedRecursively() + } +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *LabelValue) computeDiff(val *LabelValue) (ret bool) { diff --git a/examples/profile/internal/profile/line.go b/examples/profile/internal/profile/line.go index 5dac1f49..3236014e 100644 --- a/examples/profile/internal/profile/line.go +++ b/examples/profile/internal/profile/line.go @@ -89,6 +89,15 @@ func (s *Line) fixParent(parentModifiedFields *modifiedFields) { s.function.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Line) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + if !s.function.canBeShared() { + s.function.attachParent(&s.modifiedFields, fieldModifiedLineFunction) + } +} + func (s *Line) freeze() { if s.isFrozen() { return @@ -117,7 +126,7 @@ func (s *Line) SetFunction(v *Function) { } } else { if s.function.canBeShared() { - s.function = s.function.Clone(&Allocators{}) + s.function = s.function.clone(&Allocators{}) } s.function.CopyFrom(v) s.modifiedFields.markModified(fieldModifiedLineFunction) @@ -199,6 +208,15 @@ func (s *Line) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Line) clearModifiedRecursively() { + if !s.function.canBeShared() { + s.function.clearModifiedRecursively() + } + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Line) computeDiff(val *Line) (ret bool) { @@ -225,19 +243,16 @@ func (s *Line) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Line) cloneShared(allocators *Allocators) Line { - return s.Clone(allocators) -} - -func (s *Line) Clone(allocators *Allocators) Line { - c := Line{ - function: s.function.cloneShared(allocators), - line: s.line, - column: s.column, +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Line) CloneTo(dst *Line, allocators *Allocators) { + *dst = Line{ + line: s.line, + column: s.column, } - return c + dst.function = s.function.cloneShared(allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -316,12 +331,12 @@ func (s *Line) mutateRandom(random *rand.Rand, schem *schema.Schema, limiter *mu s.function = new(Function) s.function.init(&s.modifiedFields, fieldModifiedLineFunction) } else { - s.function = s.function.Clone(&Allocators{}) + s.function = s.function.clone(&Allocators{}) } } if s.function.canBeShared() { // function may be shared by pointer. Clone it to have exclusive ownership. - s.function = s.function.Clone(&Allocators{}) + s.function = s.function.clone(&Allocators{}) } s.function.mutateRandom(random, schem, limiter) diff --git a/examples/profile/internal/profile/linearray.go b/examples/profile/internal/profile/linearray.go index 9ff44955..26263cc7 100644 --- a/examples/profile/internal/profile/linearray.go +++ b/examples/profile/internal/profile/linearray.go @@ -52,6 +52,15 @@ func (e *LineArray) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *LineArray) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit + for i := range e.elems { + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + } +} + func (e *LineArray) canBeShared() bool { // An array can never be shared. return false @@ -96,6 +105,14 @@ func (e *LineArray) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *LineArray) clearModifiedRecursively() { + for i := 0; i < len(e.elems); i++ { + e.elems[i].clearModifiedRecursively() + } + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *LineArray) computeDiff(val *LineArray) (ret bool) { diff --git a/examples/profile/internal/profile/location.go b/examples/profile/internal/profile/location.go index 59b650a9..2b2068ed 100644 --- a/examples/profile/internal/profile/location.go +++ b/examples/profile/internal/profile/location.go @@ -107,6 +107,16 @@ func (s *Location) fixParent(parentModifiedFields *modifiedFields) { s.lines.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Location) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + if !s.mapping.canBeShared() { + s.mapping.attachParent(&s.modifiedFields, fieldModifiedLocationMapping) + } + s.lines.attachParent(&s.modifiedFields, fieldModifiedLocationLines) +} + // Freeze the struct. Any attempt to modify it after this will panic. // This marks the struct as eligible for safely sharing by pointer without cloning, // which can improve encoding performance. @@ -143,7 +153,7 @@ func (s *Location) SetMapping(v *Mapping) { } } else { if s.mapping.canBeShared() { - s.mapping = s.mapping.Clone(&Allocators{}) + s.mapping = s.mapping.clone(&Allocators{}) } s.mapping.CopyFrom(v) s.modifiedFields.markModified(fieldModifiedLocationMapping) @@ -246,6 +256,16 @@ func (s *Location) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Location) clearModifiedRecursively() { + if !s.mapping.canBeShared() { + s.mapping.clearModifiedRecursively() + } + s.lines.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Location) computeDiff(val *Location) (ret bool) { @@ -277,6 +297,14 @@ func (s *Location) canBeShared() bool { return s.isFrozen() } +// clone returns a clone of s allocated using allocators. +func (s *Location) clone(allocators *Allocators) *Location { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Location{}))) + c := allocators.Location.Alloc() + s.CloneTo(c, allocators) + return c +} + // cloneShared returns a clone of s. It may return s if it is safe to share without cloning // (for example if s is frozen). func (s *Location) cloneShared(allocators *Allocators) *Location { @@ -284,19 +312,20 @@ func (s *Location) cloneShared(allocators *Allocators) *Location { // If s is frozen it means it is safe to share without cloning. return s } - return s.Clone(allocators) + return s.clone(allocators) } -func (s *Location) Clone(allocators *Allocators) *Location { - allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Location{}))) - c := allocators.Location.Alloc() - *c = Location{ - mapping: s.mapping.cloneShared(allocators), +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Location) CloneTo(dst *Location, allocators *Allocators) { + *dst = Location{ address: s.address, isFolded: s.isFolded, } - copyToNewLineArray(&c.lines, &s.lines, allocators) - return c + dst.mapping = s.mapping.cloneShared(allocators) + copyToNewLineArray(&dst.lines, &s.lines, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -377,12 +406,12 @@ func (s *Location) mutateRandom(random *rand.Rand, schem *schema.Schema, limiter s.mapping = new(Mapping) s.mapping.init(&s.modifiedFields, fieldModifiedLocationMapping) } else { - s.mapping = s.mapping.Clone(&Allocators{}) + s.mapping = s.mapping.clone(&Allocators{}) } } if s.mapping.canBeShared() { // mapping may be shared by pointer. Clone it to have exclusive ownership. - s.mapping = s.mapping.Clone(&Allocators{}) + s.mapping = s.mapping.clone(&Allocators{}) } s.mapping.mutateRandom(random, schem, limiter) @@ -526,7 +555,7 @@ func (d *LocationEncoderDict) Add(val *Location) { d.dict.Set(val, refNum) return } - clone := val.Clone(d.allocators) // Clone before adding to dictionary. + clone := val.clone(d.allocators) // Clone before adding to dictionary. clone.Freeze() // Freeze the clone so that it can be safely shared by pointer. d.dict.Set(clone, refNum) } @@ -912,7 +941,7 @@ func (d *LocationDecoder) Decode(dstPtr **Location) error { // *dstPtr is pointing to a element in the dictionary. We are not allowed // to modify it. Make a clone of it and decode into the clone. - val := (*dstPtr).Clone(d.allocators) + val := (*dstPtr).clone(d.allocators) if d.allocators.allocSizeChecker.IsOverLimit() { return pkg.ErrRecordAllocLimitExceeded } diff --git a/examples/profile/internal/profile/locationarray.go b/examples/profile/internal/profile/locationarray.go index 61b57042..cbab389c 100644 --- a/examples/profile/internal/profile/locationarray.go +++ b/examples/profile/internal/profile/locationarray.go @@ -52,6 +52,17 @@ func (e *LocationArray) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *LocationArray) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit + for i := range e.elems { + if !e.elems[i].canBeShared() { + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + } + } +} + func (e *LocationArray) canBeShared() bool { // An array can never be shared. return false @@ -96,6 +107,16 @@ func (e *LocationArray) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *LocationArray) clearModifiedRecursively() { + for i := 0; i < len(e.elems); i++ { + if !e.elems[i].canBeShared() { + e.elems[i].clearModifiedRecursively() + } + } + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *LocationArray) computeDiff(val *LocationArray) (ret bool) { @@ -287,7 +308,7 @@ func (a *LocationArray) mutateRandom(random *rand.Rand, schem *schema.Schema, li if random.IntN(2*len(a.elems)) == 0 { if a.elems[i].canBeShared() { // Elem may be shared by pointer. Clone it to have exclusive ownership. - a.elems[i] = a.elems[i].Clone(&Allocators{}) + a.elems[i] = a.elems[i].clone(&Allocators{}) } a.elems[i].mutateRandom(random, schem, limiter) } diff --git a/examples/profile/internal/profile/mapping.go b/examples/profile/internal/profile/mapping.go index fd8f0b60..7fe407dc 100644 --- a/examples/profile/internal/profile/mapping.go +++ b/examples/profile/internal/profile/mapping.go @@ -113,6 +113,12 @@ func (s *Mapping) fixParent(parentModifiedFields *modifiedFields) { s.modifiedFields.parent = parentModifiedFields } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Mapping) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit +} + // Freeze the struct. Any attempt to modify it after this will panic. // This marks the struct as eligible for safely sharing by pointer without cloning, // which can improve encoding performance. @@ -361,6 +367,12 @@ func (s *Mapping) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Mapping) clearModifiedRecursively() { + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Mapping) computeDiff(val *Mapping) (ret bool) { @@ -417,6 +429,14 @@ func (s *Mapping) canBeShared() bool { return s.isFrozen() } +// clone returns a clone of s allocated using allocators. +func (s *Mapping) clone(allocators *Allocators) *Mapping { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Mapping{}))) + c := allocators.Mapping.Alloc() + s.CloneTo(c, allocators) + return c +} + // cloneShared returns a clone of s. It may return s if it is safe to share without cloning // (for example if s is frozen). func (s *Mapping) cloneShared(allocators *Allocators) *Mapping { @@ -424,13 +444,13 @@ func (s *Mapping) cloneShared(allocators *Allocators) *Mapping { // If s is frozen it means it is safe to share without cloning. return s } - return s.Clone(allocators) + return s.clone(allocators) } -func (s *Mapping) Clone(allocators *Allocators) *Mapping { - allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Mapping{}))) - c := allocators.Mapping.Alloc() - *c = Mapping{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Mapping) CloneTo(dst *Mapping, allocators *Allocators) { + *dst = Mapping{ memoryStart: s.memoryStart, memoryLimit: s.memoryLimit, fileOffset: s.fileOffset, @@ -441,7 +461,8 @@ func (s *Mapping) Clone(allocators *Allocators) *Mapping { hasLineNumbers: s.hasLineNumbers, hasInlineFrames: s.hasInlineFrames, } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -721,7 +742,7 @@ func (d *MappingEncoderDict) Add(val *Mapping) { d.dict.Set(val, refNum) return } - clone := val.Clone(d.allocators) // Clone before adding to dictionary. + clone := val.clone(d.allocators) // Clone before adding to dictionary. clone.Freeze() // Freeze the clone so that it can be safely shared by pointer. d.dict.Set(clone, refNum) } @@ -1260,7 +1281,7 @@ func (d *MappingDecoder) Decode(dstPtr **Mapping) error { // *dstPtr is pointing to a element in the dictionary. We are not allowed // to modify it. Make a clone of it and decode into the clone. - val := (*dstPtr).Clone(d.allocators) + val := (*dstPtr).clone(d.allocators) if d.allocators.allocSizeChecker.IsOverLimit() { return pkg.ErrRecordAllocLimitExceeded } diff --git a/examples/profile/internal/profile/modifiedfields.go b/examples/profile/internal/profile/modifiedfields.go index 95d09368..12bb565f 100644 --- a/examples/profile/internal/profile/modifiedfields.go +++ b/examples/profile/internal/profile/modifiedfields.go @@ -115,6 +115,13 @@ func (m *modifiedFieldsMultimap) fixParent(parentModifiedFields *modifiedFields) m.vals.parent = parentModifiedFields } +func (m *modifiedFieldsMultimap) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.keys.parent = parentModifiedFields + m.vals.parent = parentModifiedFields + m.keys.parentBit = parentModifiedBit + m.vals.parentBit = parentModifiedBit +} + // Returns a bitmask to be set in keys or vals to mark modification of // multimap element at index. func (m *modifiedFieldsMultimap) maskForIndex(index int) uint64 { diff --git a/examples/profile/internal/profile/numvalue.go b/examples/profile/internal/profile/numvalue.go index 7be0cf36..ec83bc64 100644 --- a/examples/profile/internal/profile/numvalue.go +++ b/examples/profile/internal/profile/numvalue.go @@ -80,6 +80,12 @@ func (s *NumValue) fixParent(parentModifiedFields *modifiedFields) { s.modifiedFields.parent = parentModifiedFields } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *NumValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit +} + func (s *NumValue) freeze() { s.modifiedFields.freeze() } @@ -146,6 +152,12 @@ func (s *NumValue) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *NumValue) clearModifiedRecursively() { + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *NumValue) computeDiff(val *NumValue) (ret bool) { @@ -167,18 +179,15 @@ func (s *NumValue) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *NumValue) cloneShared(allocators *Allocators) NumValue { - return s.Clone(allocators) -} - -func (s *NumValue) Clone(allocators *Allocators) NumValue { - c := NumValue{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *NumValue) CloneTo(dst *NumValue, allocators *Allocators) { + *dst = NumValue{ val: s.val, unit: s.unit, } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/examples/profile/internal/profile/profilemetadata.go b/examples/profile/internal/profile/profilemetadata.go index 25f8763b..eb270879 100644 --- a/examples/profile/internal/profile/profilemetadata.go +++ b/examples/profile/internal/profile/profilemetadata.go @@ -113,6 +113,19 @@ func (s *ProfileMetadata) fixParent(parentModifiedFields *modifiedFields) { s.defaultSampleType.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *ProfileMetadata) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + if !s.periodType.canBeShared() { + s.periodType.attachParent(&s.modifiedFields, fieldModifiedProfileMetadataPeriodType) + } + s.comments.attachParent(&s.modifiedFields, fieldModifiedProfileMetadataComments) + if !s.defaultSampleType.canBeShared() { + s.defaultSampleType.attachParent(&s.modifiedFields, fieldModifiedProfileMetadataDefaultSampleType) + } +} + func (s *ProfileMetadata) freeze() { if s.isFrozen() { return @@ -239,7 +252,7 @@ func (s *ProfileMetadata) SetPeriodType(v *SampleValueType) { } } else { if s.periodType.canBeShared() { - s.periodType = s.periodType.Clone(&Allocators{}) + s.periodType = s.periodType.clone(&Allocators{}) } s.periodType.CopyFrom(v) s.modifiedFields.markModified(fieldModifiedProfileMetadataPeriodType) @@ -314,7 +327,7 @@ func (s *ProfileMetadata) SetDefaultSampleType(v *SampleValueType) { } } else { if s.defaultSampleType.canBeShared() { - s.defaultSampleType = s.defaultSampleType.Clone(&Allocators{}) + s.defaultSampleType = s.defaultSampleType.clone(&Allocators{}) } s.defaultSampleType.CopyFrom(v) s.modifiedFields.markModified(fieldModifiedProfileMetadataDefaultSampleType) @@ -361,6 +374,19 @@ func (s *ProfileMetadata) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *ProfileMetadata) clearModifiedRecursively() { + if !s.periodType.canBeShared() { + s.periodType.clearModifiedRecursively() + } + s.comments.clearModifiedRecursively() + if !s.defaultSampleType.canBeShared() { + s.defaultSampleType.clearModifiedRecursively() + } + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *ProfileMetadata) computeDiff(val *ProfileMetadata) (ret bool) { @@ -412,24 +438,21 @@ func (s *ProfileMetadata) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *ProfileMetadata) cloneShared(allocators *Allocators) ProfileMetadata { - return s.Clone(allocators) -} - -func (s *ProfileMetadata) Clone(allocators *Allocators) ProfileMetadata { - c := ProfileMetadata{ - dropFrames: s.dropFrames, - keepFrames: s.keepFrames, - timeNanos: s.timeNanos, - durationNanos: s.durationNanos, - periodType: s.periodType.cloneShared(allocators), - period: s.period, - defaultSampleType: s.defaultSampleType.cloneShared(allocators), +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *ProfileMetadata) CloneTo(dst *ProfileMetadata, allocators *Allocators) { + *dst = ProfileMetadata{ + dropFrames: s.dropFrames, + keepFrames: s.keepFrames, + timeNanos: s.timeNanos, + durationNanos: s.durationNanos, + period: s.period, } - copyToNewStringArray(&c.comments, &s.comments, allocators) - return c + dst.periodType = s.periodType.cloneShared(allocators) + copyToNewStringArray(&dst.comments, &s.comments, allocators) + dst.defaultSampleType = s.defaultSampleType.cloneShared(allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -567,12 +590,12 @@ func (s *ProfileMetadata) mutateRandom(random *rand.Rand, schem *schema.Schema, s.periodType = new(SampleValueType) s.periodType.init(&s.modifiedFields, fieldModifiedProfileMetadataPeriodType) } else { - s.periodType = s.periodType.Clone(&Allocators{}) + s.periodType = s.periodType.clone(&Allocators{}) } } if s.periodType.canBeShared() { // periodType may be shared by pointer. Clone it to have exclusive ownership. - s.periodType = s.periodType.Clone(&Allocators{}) + s.periodType = s.periodType.clone(&Allocators{}) } s.periodType.mutateRandom(random, schem, limiter) @@ -605,12 +628,12 @@ func (s *ProfileMetadata) mutateRandom(random *rand.Rand, schem *schema.Schema, s.defaultSampleType = new(SampleValueType) s.defaultSampleType.init(&s.modifiedFields, fieldModifiedProfileMetadataDefaultSampleType) } else { - s.defaultSampleType = s.defaultSampleType.Clone(&Allocators{}) + s.defaultSampleType = s.defaultSampleType.clone(&Allocators{}) } } if s.defaultSampleType.canBeShared() { // defaultSampleType may be shared by pointer. Clone it to have exclusive ownership. - s.defaultSampleType = s.defaultSampleType.Clone(&Allocators{}) + s.defaultSampleType = s.defaultSampleType.clone(&Allocators{}) } s.defaultSampleType.mutateRandom(random, schem, limiter) diff --git a/examples/profile/internal/profile/sample.go b/examples/profile/internal/profile/sample.go index 93f155ee..16303a91 100644 --- a/examples/profile/internal/profile/sample.go +++ b/examples/profile/internal/profile/sample.go @@ -98,6 +98,16 @@ func (s *Sample) fixParent(parentModifiedFields *modifiedFields) { s.labels.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Sample) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.metadata.attachParent(&s.modifiedFields, fieldModifiedSampleMetadata) + s.locations.attachParent(&s.modifiedFields, fieldModifiedSampleLocations) + s.values.attachParent(&s.modifiedFields, fieldModifiedSampleValues) + s.labels.attachParent(&s.modifiedFields, fieldModifiedSampleLabels) +} + func (s *Sample) freeze() { if s.isFrozen() { return @@ -205,6 +215,16 @@ func (s *Sample) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Sample) clearModifiedRecursively() { + s.metadata.clearModifiedRecursively() + s.locations.clearModifiedRecursively() + s.values.clearModifiedRecursively() + s.labels.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Sample) computeDiff(val *Sample) (ret bool) { @@ -236,19 +256,16 @@ func (s *Sample) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Sample) cloneShared(allocators *Allocators) Sample { - return s.Clone(allocators) -} - -func (s *Sample) Clone(allocators *Allocators) Sample { - c := Sample{} - copyToNewProfileMetadata(&c.metadata, &s.metadata, allocators) - copyToNewLocationArray(&c.locations, &s.locations, allocators) - copyToNewSampleValueArray(&c.values, &s.values, allocators) - copyToNewLabels(&c.labels, &s.labels, allocators) - return c +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Sample) CloneTo(dst *Sample, allocators *Allocators) { + *dst = Sample{} + copyToNewProfileMetadata(&dst.metadata, &s.metadata, allocators) + copyToNewLocationArray(&dst.locations, &s.locations, allocators) + copyToNewSampleValueArray(&dst.values, &s.values, allocators) + copyToNewLabels(&dst.labels, &s.labels, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/examples/profile/internal/profile/samplevalue.go b/examples/profile/internal/profile/samplevalue.go index 2238a010..50d54ca1 100644 --- a/examples/profile/internal/profile/samplevalue.go +++ b/examples/profile/internal/profile/samplevalue.go @@ -86,6 +86,15 @@ func (s *SampleValue) fixParent(parentModifiedFields *modifiedFields) { s.type_.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *SampleValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + if !s.type_.canBeShared() { + s.type_.attachParent(&s.modifiedFields, fieldModifiedSampleValueType) + } +} + func (s *SampleValue) freeze() { if s.isFrozen() { return @@ -138,7 +147,7 @@ func (s *SampleValue) SetType(v *SampleValueType) { } } else { if s.type_.canBeShared() { - s.type_ = s.type_.Clone(&Allocators{}) + s.type_ = s.type_.clone(&Allocators{}) } s.type_.CopyFrom(v) s.modifiedFields.markModified(fieldModifiedSampleValueType) @@ -171,6 +180,15 @@ func (s *SampleValue) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *SampleValue) clearModifiedRecursively() { + if !s.type_.canBeShared() { + s.type_.clearModifiedRecursively() + } + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *SampleValue) computeDiff(val *SampleValue) (ret bool) { @@ -192,18 +210,15 @@ func (s *SampleValue) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *SampleValue) cloneShared(allocators *Allocators) SampleValue { - return s.Clone(allocators) -} - -func (s *SampleValue) Clone(allocators *Allocators) SampleValue { - c := SampleValue{ - val: s.val, - type_: s.type_.cloneShared(allocators), +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *SampleValue) CloneTo(dst *SampleValue, allocators *Allocators) { + *dst = SampleValue{ + val: s.val, } - return c + dst.type_ = s.type_.cloneShared(allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -287,12 +302,12 @@ func (s *SampleValue) mutateRandom(random *rand.Rand, schem *schema.Schema, limi s.type_ = new(SampleValueType) s.type_.init(&s.modifiedFields, fieldModifiedSampleValueType) } else { - s.type_ = s.type_.Clone(&Allocators{}) + s.type_ = s.type_.clone(&Allocators{}) } } if s.type_.canBeShared() { // type_ may be shared by pointer. Clone it to have exclusive ownership. - s.type_ = s.type_.Clone(&Allocators{}) + s.type_ = s.type_.clone(&Allocators{}) } s.type_.mutateRandom(random, schem, limiter) diff --git a/examples/profile/internal/profile/samplevaluearray.go b/examples/profile/internal/profile/samplevaluearray.go index a441d282..d9cd2d68 100644 --- a/examples/profile/internal/profile/samplevaluearray.go +++ b/examples/profile/internal/profile/samplevaluearray.go @@ -52,6 +52,15 @@ func (e *SampleValueArray) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *SampleValueArray) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit + for i := range e.elems { + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + } +} + func (e *SampleValueArray) canBeShared() bool { // An array can never be shared. return false @@ -96,6 +105,14 @@ func (e *SampleValueArray) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *SampleValueArray) clearModifiedRecursively() { + for i := 0; i < len(e.elems); i++ { + e.elems[i].clearModifiedRecursively() + } + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *SampleValueArray) computeDiff(val *SampleValueArray) (ret bool) { diff --git a/examples/profile/internal/profile/samplevaluetype.go b/examples/profile/internal/profile/samplevaluetype.go index 42e7387a..85e1411d 100644 --- a/examples/profile/internal/profile/samplevaluetype.go +++ b/examples/profile/internal/profile/samplevaluetype.go @@ -92,6 +92,12 @@ func (s *SampleValueType) fixParent(parentModifiedFields *modifiedFields) { s.modifiedFields.parent = parentModifiedFields } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *SampleValueType) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit +} + // Freeze the struct. Any attempt to modify it after this will panic. // This marks the struct as eligible for safely sharing by pointer without cloning, // which can improve encoding performance. @@ -165,6 +171,12 @@ func (s *SampleValueType) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *SampleValueType) clearModifiedRecursively() { + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *SampleValueType) computeDiff(val *SampleValueType) (ret bool) { @@ -186,6 +198,14 @@ func (s *SampleValueType) canBeShared() bool { return s.isFrozen() } +// clone returns a clone of s allocated using allocators. +func (s *SampleValueType) clone(allocators *Allocators) *SampleValueType { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(SampleValueType{}))) + c := allocators.SampleValueType.Alloc() + s.CloneTo(c, allocators) + return c +} + // cloneShared returns a clone of s. It may return s if it is safe to share without cloning // (for example if s is frozen). func (s *SampleValueType) cloneShared(allocators *Allocators) *SampleValueType { @@ -193,17 +213,18 @@ func (s *SampleValueType) cloneShared(allocators *Allocators) *SampleValueType { // If s is frozen it means it is safe to share without cloning. return s } - return s.Clone(allocators) + return s.clone(allocators) } -func (s *SampleValueType) Clone(allocators *Allocators) *SampleValueType { - allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(SampleValueType{}))) - c := allocators.SampleValueType.Alloc() - *c = SampleValueType{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *SampleValueType) CloneTo(dst *SampleValueType, allocators *Allocators) { + *dst = SampleValueType{ type_: s.type_, unit: s.unit, } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -357,7 +378,7 @@ func (d *SampleValueTypeEncoderDict) Add(val *SampleValueType) { d.dict.Set(val, refNum) return } - clone := val.Clone(d.allocators) // Clone before adding to dictionary. + clone := val.clone(d.allocators) // Clone before adding to dictionary. clone.Freeze() // Freeze the clone so that it can be safely shared by pointer. d.dict.Set(clone, refNum) } @@ -609,7 +630,7 @@ func (d *SampleValueTypeDecoder) Decode(dstPtr **SampleValueType) error { // *dstPtr is pointing to a element in the dictionary. We are not allowed // to modify it. Make a clone of it and decode into the clone. - val := (*dstPtr).Clone(d.allocators) + val := (*dstPtr).clone(d.allocators) if d.allocators.allocSizeChecker.IsOverLimit() { return pkg.ErrRecordAllocLimitExceeded } diff --git a/examples/profile/internal/profile/stringarray.go b/examples/profile/internal/profile/stringarray.go index 2511d8fb..8e84370f 100644 --- a/examples/profile/internal/profile/stringarray.go +++ b/examples/profile/internal/profile/stringarray.go @@ -51,6 +51,12 @@ func (e *StringArray) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *StringArray) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit +} + func (e *StringArray) canBeShared() bool { // An array can never be shared. return false @@ -97,6 +103,11 @@ func (e *StringArray) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *StringArray) clearModifiedRecursively() { + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *StringArray) computeDiff(val *StringArray) (ret bool) { diff --git a/go/otel/otel.stef b/go/otel/otel.stef index 644dca23..76e8b38e 100644 --- a/go/otel/otel.stef +++ b/go/otel/otel.stef @@ -1,7 +1,7 @@ // This is Otel/STEF schema: a representation of OpenTelemetry data model // as STEF records. Data model is virtually a direct mapping from OpenTelemetry -// Protobuf IDL for metrics and traces, see: https://github.com/open-telemetry/opentelemetry-proto/tree/main/opentelemetry/proto -// TODO: add Logs and Profiles. +// Protobuf IDL for metrics, logs and traces, see: https://github.com/open-telemetry/opentelemetry-proto/tree/main/opentelemetry/proto +// TODO: add Profiles. package com.example.otelstef @@ -213,3 +213,56 @@ struct SpanStatus { Message string Code uint64 } + +//////////////////////////////////////////////////////////////// +// Log signal schema. +//////////////////////////////////////////////////////////////// + +struct Logs root { + Envelope Envelope + Resource Resource + Scope Scope + Log LogRecord +} + +struct LogRecord { + TimeUnixNano uint64 + ObservedTimeUnixNano uint64 + SeverityNumber SeverityNumber + SeverityText string dict(LogSeverityText) + Body AnyValue + Attributes Attributes + DroppedAttributesCount uint64 + Flags uint64 + TraceID bytes + SpanID bytes + EventName string dict(LogEventName) +} + +enum SeverityNumber { + Unspecified = 0 + Trace = 1 + Trace2 = 2 + Trace3 = 3 + Trace4 = 4 + Debug = 5 + Debug2 = 6 + Debug3 = 7 + Debug4 = 8 + Info = 9 + Info2 = 10 + Info3 = 11 + Info4 = 12 + Warn = 13 + Warn2 = 14 + Warn3 = 15 + Warn4 = 16 + Error = 17 + Error2 = 18 + Error3 = 19 + Error4 = 20 + Fatal = 21 + Fatal2 = 22 + Fatal3 = 23 + Fatal4 = 24 +} diff --git a/go/otel/otelstef/anyvalue.go b/go/otel/otelstef/anyvalue.go index 8920c8a6..d173016e 100644 --- a/go/otel/otelstef/anyvalue.go +++ b/go/otel/otelstef/anyvalue.go @@ -82,6 +82,19 @@ func (s *AnyValue) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointer and bit in a newly copied oneof. +func (s *AnyValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.parentModifiedFields = parentModifiedFields + s.parentModifiedBit = parentModifiedBit + + switch s.Type() { + case AnyValueTypeArray: + s.arrayPtr().attachParent(parentModifiedFields, parentModifiedBit) + case AnyValueTypeKVList: + s.kVListPtr().attachParent(parentModifiedFields, parentModifiedBit) + } +} + type AnyValueType byte const ( @@ -287,36 +300,45 @@ func (s *AnyValue) canBeShared() bool { return false } +func (s *AnyValue) clone(allocators *Allocators) *AnyValue { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(AnyValue{}))) + c := allocators.AnyValue.Alloc() + s.CloneTo(c, allocators) + return c +} + func (s *AnyValue) cloneShared(allocators *Allocators) *AnyValue { - // Oneof is not shareable, so cloneShared is just a Clone. - return s.Clone(allocators) + // Oneof is not shareable, so cloneShared is just a clone. + return s.clone(allocators) } -func (s *AnyValue) Clone(allocators *Allocators) *AnyValue { - allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(AnyValue{}))) - c := allocators.AnyValue.Alloc() - c.clearValSetType(s.Type()) +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *AnyValue) CloneTo(dst *AnyValue, allocators *Allocators) { + *dst = AnyValue{} + dst.clearValSetType(s.Type()) switch s.Type() { case AnyValueTypeString: - c.setString(s.String()) + dst.setString(s.String()) case AnyValueTypeBool: - *c.boolPtr() = *s.boolPtr() + *dst.boolPtr() = *s.boolPtr() case AnyValueTypeInt64: - *c.int64Ptr() = *s.int64Ptr() + *dst.int64Ptr() = *s.int64Ptr() case AnyValueTypeFloat64: - *c.float64Ptr() = *s.float64Ptr() + *dst.float64Ptr() = *s.float64Ptr() case AnyValueTypeArray: allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(AnyValueArray{}))) - c.allocArrayAlloc(allocators) - copyToNewAnyValueArray(c.arrayPtr(), s.arrayPtr(), allocators) + dst.allocArrayAlloc(allocators) + copyToNewAnyValueArray(dst.arrayPtr(), s.arrayPtr(), allocators) case AnyValueTypeKVList: allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(KeyValueList{}))) - c.allocKVListAlloc(allocators) - copyToNewKeyValueList(c.kVListPtr(), s.kVListPtr(), allocators) + dst.allocKVListAlloc(allocators) + copyToNewKeyValueList(dst.kVListPtr(), s.kVListPtr(), allocators) case AnyValueTypeBytes: - c.setBytes(s.Bytes()) + dst.setBytes(s.Bytes()) } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -433,6 +455,16 @@ func (s *AnyValue) setUnmodifiedRecursively() { } } +// clearModifiedRecursively clears modification state in all mutable descendants. +func (s *AnyValue) clearModifiedRecursively() { + switch s.Type() { + case AnyValueTypeArray: + s.arrayPtr().clearModifiedRecursively() + case AnyValueTypeKVList: + s.kVListPtr().clearModifiedRecursively() + } +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *AnyValue) computeDiff(val *AnyValue) (ret bool) { diff --git a/go/otel/otelstef/anyvaluearray.go b/go/otel/otelstef/anyvaluearray.go index f234dd27..8451f48e 100644 --- a/go/otel/otelstef/anyvaluearray.go +++ b/go/otel/otelstef/anyvaluearray.go @@ -52,6 +52,15 @@ func (e *AnyValueArray) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *AnyValueArray) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit + for i := range e.elems { + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + } +} + func (e *AnyValueArray) canBeShared() bool { // An array can never be shared. return false @@ -96,6 +105,14 @@ func (e *AnyValueArray) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *AnyValueArray) clearModifiedRecursively() { + for i := 0; i < len(e.elems); i++ { + e.elems[i].clearModifiedRecursively() + } + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *AnyValueArray) computeDiff(val *AnyValueArray) (ret bool) { diff --git a/go/otel/otelstef/attributes.go b/go/otel/otelstef/attributes.go index 839f3660..b8017d14 100644 --- a/go/otel/otelstef/attributes.go +++ b/go/otel/otelstef/attributes.go @@ -60,6 +60,15 @@ func (m *Attributes) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointers and bits in a newly copied multimap. +func (m *Attributes) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.modifiedElems.attachParent(parentModifiedFields, parentModifiedBit) + elems := m.elems[:m.initedCount] + for i := 0; i < len(elems); i++ { + elems[i].value.attachParent(&m.modifiedElems.vals, m.modifiedElems.maskForIndex(i)) + } +} + func (m *Attributes) canBeShared() bool { // Multimap can never be shared. return false @@ -122,6 +131,14 @@ func (m *Attributes) setUnmodifiedRecursively() { m.modifiedElems.setUnmodifiedAll() } +// clearModifiedRecursively clears modification state in all mutable elements. +func (m *Attributes) clearModifiedRecursively() { + for i := 0; i < len(m.elems); i++ { + m.elems[i].value.clearModifiedRecursively() + } + m.modifiedElems.setUnmodifiedAll() +} + // computeDiff compares m to val and returns true if they differ. // All fields that are different in m will be marked as modified. func (m *Attributes) computeDiff(val *Attributes) (ret bool) { diff --git a/go/otel/otelstef/clone_test.go b/go/otel/otelstef/clone_test.go new file mode 100644 index 00000000..e6d41383 --- /dev/null +++ b/go/otel/otelstef/clone_test.go @@ -0,0 +1,74 @@ +package otelstef + +import ( + "bytes" + "testing" + + "github.com/splunk/stef/go/pkg" +) + +func TestLogsCloneTracksExistingAttributeValueModifications(t *testing.T) { + var source Logs + source.Init() + source.Log().Attributes().EnsureLen(1) + source.Log().Attributes().SetKey(0, "key") + source.Log().Attributes().Value(0).SetInt64(1) + + var allocators Allocators + var copy Logs + source.CloneTo(©, &allocators) + + if copy.IsLogModified() || copy.Log().IsAttributesModified() { + t.Fatal("clone is unexpectedly marked modified") + } + + copy.Log().Attributes().Value(0).SetInt64(2) + + if !copy.IsLogModified() || !copy.Log().IsAttributesModified() { + t.Fatal("attribute value modification did not propagate through clone") + } +} + +func TestLogsCloneModificationSurvivesEncoding(t *testing.T) { + var buf pkg.MemChunkWriter + writer, err := NewLogsWriter(&buf, pkg.WriterOptions{}) + if err != nil { + t.Fatal(err) + } + + writer.Record.Log().Attributes().EnsureLen(1) + writer.Record.Log().Attributes().SetKey(0, "key") + writer.Record.Log().Attributes().Value(0).SetInt64(1) + if err := writer.Write(); err != nil { + t.Fatal(err) + } + + var allocators Allocators + var cloned Logs + writer.Record.CloneTo(&cloned, &allocators) + cloned.Log().Attributes().Value(0).SetInt64(2) + writer.Record.CopyFrom(&cloned) + if err := writer.Write(); err != nil { + t.Fatal(err) + } + if err := writer.Flush(); err != nil { + t.Fatal(err) + } + + reader, err := NewLogsReader(bytes.NewReader(buf.Bytes())) + if err != nil { + t.Fatal(err) + } + if err := reader.Read(pkg.ReadOptions{}); err != nil { + t.Fatal(err) + } + if got := reader.Record.Log().Attributes().Value(0).Int64(); got != 1 { + t.Fatalf("first attribute value = %d, want 1", got) + } + if err := reader.Read(pkg.ReadOptions{}); err != nil { + t.Fatal(err) + } + if got := reader.Record.Log().Attributes().Value(0).Int64(); got != 2 { + t.Fatalf("cloned attribute value after encoding = %d, want 2", got) + } +} diff --git a/go/otel/otelstef/common.go b/go/otel/otelstef/common.go index b64af1a5..a62207be 100644 --- a/go/otel/otelstef/common.go +++ b/go/otel/otelstef/common.go @@ -10,7 +10,7 @@ import ( ) // The STEF IDL string. Used for testing. -const allSchemaContent = "// This is Otel/STEF schema: a representation of OpenTelemetry data model\n// as STEF records. Data model is virtually a direct mapping from OpenTelemetry\n// Protobuf IDL for metrics and traces, see: https://github.com/open-telemetry/opentelemetry-proto/tree/main/opentelemetry/proto\n// TODO: add Logs and Profiles.\n\npackage com.example.otelstef\n\n////////////////////////////////////////////////////////////////\n// Schema that is common across all signals.\n////////////////////////////////////////////////////////////////\n\nmultimap Attributes {\n key string dict(AttributeKey)\n value AnyValue\n}\n\noneof AnyValue {\n String string dict(AnyValueString)\n Bool bool\n Int64 int64\n Float64 float64\n Array []AnyValue\n KVList KeyValueList\n Bytes bytes\n}\n\nmultimap KeyValueList {\n key string\n value AnyValue\n}\n\nstruct Resource dict(Resource) {\n // All SchemaURL fields use the same (shared) dict.\n SchemaURL string dict(SchemaURL)\n Attributes Attributes\n DroppedAttributesCount uint64\n}\n\nstruct Scope dict(Scope) {\n Name string dict(ScopeName)\n Version string dict(ScopeVersion)\n SchemaURL string dict(SchemaURL)\n Attributes Attributes\n DroppedAttributesCount uint64\n}\n\nstruct Envelope {\n Attributes EnvelopeAttributes\n}\n\nmultimap EnvelopeAttributes {\n key string\n value bytes\n}\n\n////////////////////////////////////////////////////////////////\n// Metric signal schema.\n////////////////////////////////////////////////////////////////\n\nstruct Metrics root {\n Envelope Envelope\n Metric Metric\n Resource Resource\n Scope Scope\n Attributes Attributes\n Point Point\n}\n\nstruct Metric dict(Metric) {\n Name string dict(MetricName)\n Description string dict(MetricDescription)\n Unit string dict(MetricUnit)\n Type MetricType\n Metadata Attributes\n HistogramBounds []float64\n AggregationTemporality AggregationTemporality\n Monotonic bool\n}\n\nenum MetricType {\n\tGauge = 0\n\tSum = 1\n\tHistogram = 2\n\tExpHistogram = 3\n\tSummary = 4\n}\n\nenum AggregationTemporality {\n Unspecified = 0\n Delta = 1\n Cumulative = 2\n}\n\n// Point represents a metric data point.\nstruct Point {\n StartTimestamp uint64\n Timestamp uint64\n Value PointValue\n Exemplars []Exemplar\n}\n\noneof PointValue {\n Int64 int64\n Float64 float64\n Histogram HistogramValue\n ExpHistogram ExpHistogramValue\n Summary SummaryValue\n}\n\nstruct HistogramValue {\n Count int64\n Sum float64 optional\n Min float64 optional\n Max float64 optional\n BucketCounts []uint64\n}\n\nstruct ExpHistogramValue {\n Count uint64\n Sum float64 optional\n Min float64 optional\n Max float64 optional\n Scale int64\n ZeroCount uint64\n PositiveBuckets ExpHistogramBuckets\n NegativeBuckets ExpHistogramBuckets\n ZeroThreshold float64\n}\n\nstruct ExpHistogramBuckets {\n Offset int64\n BucketCounts []uint64\n}\n\nstruct SummaryValue {\n Count uint64\n Sum float64\n QuantileValues []QuantileValue\n}\n\nstruct QuantileValue {\n Quantile float64\n Value float64\n}\n\nstruct Exemplar {\n Timestamp uint64\n Value ExemplarValue\n SpanID bytes\n TraceID bytes\n FilteredAttributes Attributes\n}\n\noneof ExemplarValue {\n Int64 int64\n Float64 float64\n}\n\n////////////////////////////////////////////////////////////////\n// Trace signal schema.\n////////////////////////////////////////////////////////////////\n\nstruct Spans root {\n Envelope Envelope\n Resource Resource\n Scope Scope\n Span Span\n}\n\nstruct Span {\n TraceID bytes\n SpanID bytes\n TraceState string\n ParentSpanID bytes\n Flags uint64\n Name string dict(SpanName)\n Kind SpanKind\n StartTimeUnixNano uint64\n EndTimeUnixNano uint64\n Attributes Attributes\n DroppedAttributesCount uint64\n Events []Event\n Links []Link\n Status SpanStatus\n}\n\nenum SpanKind {\n Unspecified = 0\n Internal = 1\n Server = 2\n Client = 3\n Producer = 4\n Consumer = 5\n}\n\nstruct Link {\n TraceID bytes\n SpanID bytes\n TraceState string\n Flags uint64\n Attributes Attributes\n DroppedAttributesCount uint64\n}\n\nstruct Event {\n Name string dict(SpanEventName)\n TimeUnixNano uint64\n Attributes Attributes\n DroppedAttributesCount uint64\n}\n\nstruct SpanStatus {\n Message string\n Code uint64\n}\n" +const allSchemaContent = "// This is Otel/STEF schema: a representation of OpenTelemetry data model\n// as STEF records. Data model is virtually a direct mapping from OpenTelemetry\n// Protobuf IDL for metrics, logs and traces, see: https://github.com/open-telemetry/opentelemetry-proto/tree/main/opentelemetry/proto\n// TODO: add Profiles.\n\npackage com.example.otelstef\n\n////////////////////////////////////////////////////////////////\n// Schema that is common across all signals.\n////////////////////////////////////////////////////////////////\n\nmultimap Attributes {\n key string dict(AttributeKey)\n value AnyValue\n}\n\noneof AnyValue {\n String string dict(AnyValueString)\n Bool bool\n Int64 int64\n Float64 float64\n Array []AnyValue\n KVList KeyValueList\n Bytes bytes\n}\n\nmultimap KeyValueList {\n key string\n value AnyValue\n}\n\nstruct Resource dict(Resource) {\n // All SchemaURL fields use the same (shared) dict.\n SchemaURL string dict(SchemaURL)\n Attributes Attributes\n DroppedAttributesCount uint64\n}\n\nstruct Scope dict(Scope) {\n Name string dict(ScopeName)\n Version string dict(ScopeVersion)\n SchemaURL string dict(SchemaURL)\n Attributes Attributes\n DroppedAttributesCount uint64\n}\n\nstruct Envelope {\n Attributes EnvelopeAttributes\n}\n\nmultimap EnvelopeAttributes {\n key string\n value bytes\n}\n\n////////////////////////////////////////////////////////////////\n// Metric signal schema.\n////////////////////////////////////////////////////////////////\n\nstruct Metrics root {\n Envelope Envelope\n Metric Metric\n Resource Resource\n Scope Scope\n Attributes Attributes\n Point Point\n}\n\nstruct Metric dict(Metric) {\n Name string dict(MetricName)\n Description string dict(MetricDescription)\n Unit string dict(MetricUnit)\n Type MetricType\n Metadata Attributes\n HistogramBounds []float64\n AggregationTemporality AggregationTemporality\n Monotonic bool\n}\n\nenum MetricType {\n\tGauge = 0\n\tSum = 1\n\tHistogram = 2\n\tExpHistogram = 3\n\tSummary = 4\n}\n\nenum AggregationTemporality {\n Unspecified = 0\n Delta = 1\n Cumulative = 2\n}\n\n// Point represents a metric data point.\nstruct Point {\n StartTimestamp uint64\n Timestamp uint64\n Value PointValue\n Exemplars []Exemplar\n}\n\noneof PointValue {\n Int64 int64\n Float64 float64\n Histogram HistogramValue\n ExpHistogram ExpHistogramValue\n Summary SummaryValue\n}\n\nstruct HistogramValue {\n Count int64\n Sum float64 optional\n Min float64 optional\n Max float64 optional\n BucketCounts []uint64\n}\n\nstruct ExpHistogramValue {\n Count uint64\n Sum float64 optional\n Min float64 optional\n Max float64 optional\n Scale int64\n ZeroCount uint64\n PositiveBuckets ExpHistogramBuckets\n NegativeBuckets ExpHistogramBuckets\n ZeroThreshold float64\n}\n\nstruct ExpHistogramBuckets {\n Offset int64\n BucketCounts []uint64\n}\n\nstruct SummaryValue {\n Count uint64\n Sum float64\n QuantileValues []QuantileValue\n}\n\nstruct QuantileValue {\n Quantile float64\n Value float64\n}\n\nstruct Exemplar {\n Timestamp uint64\n Value ExemplarValue\n SpanID bytes\n TraceID bytes\n FilteredAttributes Attributes\n}\n\noneof ExemplarValue {\n Int64 int64\n Float64 float64\n}\n\n////////////////////////////////////////////////////////////////\n// Trace signal schema.\n////////////////////////////////////////////////////////////////\n\nstruct Spans root {\n Envelope Envelope\n Resource Resource\n Scope Scope\n Span Span\n}\n\nstruct Span {\n TraceID bytes\n SpanID bytes\n TraceState string\n ParentSpanID bytes\n Flags uint64\n Name string dict(SpanName)\n Kind SpanKind\n StartTimeUnixNano uint64\n EndTimeUnixNano uint64\n Attributes Attributes\n DroppedAttributesCount uint64\n Events []Event\n Links []Link\n Status SpanStatus\n}\n\nenum SpanKind {\n Unspecified = 0\n Internal = 1\n Server = 2\n Client = 3\n Producer = 4\n Consumer = 5\n}\n\nstruct Link {\n TraceID bytes\n SpanID bytes\n TraceState string\n Flags uint64\n Attributes Attributes\n DroppedAttributesCount uint64\n}\n\nstruct Event {\n Name string dict(SpanEventName)\n TimeUnixNano uint64\n Attributes Attributes\n DroppedAttributesCount uint64\n}\n\nstruct SpanStatus {\n Message string\n Code uint64\n}\n\n////////////////////////////////////////////////////////////////\n// Log signal schema.\n////////////////////////////////////////////////////////////////\n\nstruct Logs root {\n Envelope Envelope\n Resource Resource\n Scope Scope\n Log LogRecord\n}\n\nstruct LogRecord {\n TimeUnixNano uint64\n ObservedTimeUnixNano uint64\n SeverityNumber SeverityNumber\n SeverityText string dict(LogSeverityText)\n Body AnyValue\n Attributes Attributes\n DroppedAttributesCount uint64\n Flags uint64\n TraceID bytes\n SpanID bytes\n EventName string dict(LogEventName)\n}\n\nenum SeverityNumber {\n Unspecified = 0\n Trace = 1\n Trace2 = 2\n Trace3 = 3\n Trace4 = 4\n Debug = 5\n Debug2 = 6\n Debug3 = 7\n Debug4 = 8\n Info = 9\n Info2 = 10\n Info3 = 11\n Info4 = 12\n Warn = 13\n Warn2 = 14\n Warn3 = 15\n Warn4 = 16\n Error = 17\n Error2 = 18\n Error3 = 19\n Error4 = 20\n Fatal = 21\n Fatal2 = 22\n Fatal3 = 23\n Fatal4 = 24\n}\n" // StructFieldCounts is used to keep track of the number of fields in each struct // of an associated schema and to iterate over the fields. @@ -27,6 +27,8 @@ type StructFieldCounts struct { countExpHistogramValue uint // Number of fields in ExpHistogramValue struct. countHistogramValue uint // Number of fields in HistogramValue struct. countLink uint // Number of fields in Link struct. + countLogRecord uint // Number of fields in LogRecord struct. + countLogs uint // Number of fields in Logs struct. countMetric uint // Number of fields in Metric struct. countMetrics uint // Number of fields in Metrics struct. countPoint uint // Number of fields in Point struct. @@ -61,6 +63,8 @@ func (s *StructFieldCounts) Init(overrideSchema *schema.WireSchema) { s.countExpHistogramValue = math.MaxUint s.countHistogramValue = math.MaxUint s.countLink = math.MaxUint + s.countLogRecord = math.MaxUint + s.countLogs = math.MaxUint s.countMetric = math.MaxUint s.countMetrics = math.MaxUint s.countPoint = math.MaxUint @@ -134,6 +138,14 @@ func (s *StructFieldCounts) LinkFieldCount() (uint, error) { return getFieldCount(&s.countLink, "Link", s.overrideSchema, &s.overrideSchemaIter, 6) } +func (s *StructFieldCounts) LogRecordFieldCount() (uint, error) { + return getFieldCount(&s.countLogRecord, "LogRecord", s.overrideSchema, &s.overrideSchemaIter, 11) +} + +func (s *StructFieldCounts) LogsFieldCount() (uint, error) { + return getFieldCount(&s.countLogs, "Logs", s.overrideSchema, &s.overrideSchemaIter, 4) +} + func (s *StructFieldCounts) MetricFieldCount() (uint, error) { return getFieldCount(&s.countMetric, "Metric", s.overrideSchema, &s.overrideSchemaIter, 8) } @@ -192,6 +204,8 @@ type Allocators struct { ExpHistogramValue ExpHistogramValueAllocator HistogramValue HistogramValueAllocator Link LinkAllocator + LogRecord LogRecordAllocator + Logs LogsAllocator Metric MetricAllocator Metrics MetricsAllocator Point PointAllocator diff --git a/go/otel/otelstef/envelope.go b/go/otel/otelstef/envelope.go index 2862a67a..05ae8e56 100644 --- a/go/otel/otelstef/envelope.go +++ b/go/otel/otelstef/envelope.go @@ -80,6 +80,13 @@ func (s *Envelope) fixParent(parentModifiedFields *modifiedFields) { s.attributes.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Envelope) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.attributes.attachParent(&s.modifiedFields, fieldModifiedEnvelopeAttributes) +} + func (s *Envelope) freeze() { if s.isFrozen() { return @@ -121,6 +128,13 @@ func (s *Envelope) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Envelope) clearModifiedRecursively() { + s.attributes.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Envelope) computeDiff(val *Envelope) (ret bool) { @@ -137,16 +151,13 @@ func (s *Envelope) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Envelope) cloneShared(allocators *Allocators) Envelope { - return s.Clone(allocators) -} - -func (s *Envelope) Clone(allocators *Allocators) Envelope { - c := Envelope{} - copyToNewEnvelopeAttributes(&c.attributes, &s.attributes, allocators) - return c +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Envelope) CloneTo(dst *Envelope, allocators *Allocators) { + *dst = Envelope{} + copyToNewEnvelopeAttributes(&dst.attributes, &s.attributes, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/envelopeattributes.go b/go/otel/otelstef/envelopeattributes.go index df7ef2ea..266eec9a 100644 --- a/go/otel/otelstef/envelopeattributes.go +++ b/go/otel/otelstef/envelopeattributes.go @@ -58,6 +58,14 @@ func (m *EnvelopeAttributes) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointers and bits in a newly copied multimap. +func (m *EnvelopeAttributes) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.modifiedElems.attachParent(parentModifiedFields, parentModifiedBit) + elems := m.elems[:m.initedCount] + for i := 0; i < len(elems); i++ { + } +} + func (m *EnvelopeAttributes) canBeShared() bool { // Multimap can never be shared. return false @@ -113,6 +121,13 @@ func (m *EnvelopeAttributes) setUnmodifiedRecursively() { m.modifiedElems.setUnmodifiedAll() } +// clearModifiedRecursively clears modification state in all mutable elements. +func (m *EnvelopeAttributes) clearModifiedRecursively() { + for i := 0; i < len(m.elems); i++ { + } + m.modifiedElems.setUnmodifiedAll() +} + // computeDiff compares m to val and returns true if they differ. // All fields that are different in m will be marked as modified. func (m *EnvelopeAttributes) computeDiff(val *EnvelopeAttributes) (ret bool) { diff --git a/go/otel/otelstef/event.go b/go/otel/otelstef/event.go index f371c7ab..f6e75f90 100644 --- a/go/otel/otelstef/event.go +++ b/go/otel/otelstef/event.go @@ -89,6 +89,13 @@ func (s *Event) fixParent(parentModifiedFields *modifiedFields) { s.attributes.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Event) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.attributes.attachParent(&s.modifiedFields, fieldModifiedEventAttributes) +} + func (s *Event) freeze() { if s.isFrozen() { return @@ -205,6 +212,13 @@ func (s *Event) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Event) clearModifiedRecursively() { + s.attributes.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Event) computeDiff(val *Event) (ret bool) { @@ -236,20 +250,17 @@ func (s *Event) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Event) cloneShared(allocators *Allocators) Event { - return s.Clone(allocators) -} - -func (s *Event) Clone(allocators *Allocators) Event { - c := Event{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Event) CloneTo(dst *Event, allocators *Allocators) { + *dst = Event{ name: s.name, timeUnixNano: s.timeUnixNano, droppedAttributesCount: s.droppedAttributesCount, } - copyToNewAttributes(&c.attributes, &s.attributes, allocators) - return c + copyToNewAttributes(&dst.attributes, &s.attributes, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/eventarray.go b/go/otel/otelstef/eventarray.go index 3b0c59b2..e328e599 100644 --- a/go/otel/otelstef/eventarray.go +++ b/go/otel/otelstef/eventarray.go @@ -52,6 +52,15 @@ func (e *EventArray) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *EventArray) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit + for i := range e.elems { + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + } +} + func (e *EventArray) canBeShared() bool { // An array can never be shared. return false @@ -96,6 +105,14 @@ func (e *EventArray) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *EventArray) clearModifiedRecursively() { + for i := 0; i < len(e.elems); i++ { + e.elems[i].clearModifiedRecursively() + } + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *EventArray) computeDiff(val *EventArray) (ret bool) { diff --git a/go/otel/otelstef/exemplar.go b/go/otel/otelstef/exemplar.go index bd7ea3fb..8dce6782 100644 --- a/go/otel/otelstef/exemplar.go +++ b/go/otel/otelstef/exemplar.go @@ -95,6 +95,14 @@ func (s *Exemplar) fixParent(parentModifiedFields *modifiedFields) { s.filteredAttributes.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Exemplar) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.value.attachParent(&s.modifiedFields, fieldModifiedExemplarValue) + s.filteredAttributes.attachParent(&s.modifiedFields, fieldModifiedExemplarFilteredAttributes) +} + func (s *Exemplar) freeze() { if s.isFrozen() { return @@ -233,6 +241,14 @@ func (s *Exemplar) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Exemplar) clearModifiedRecursively() { + s.value.clearModifiedRecursively() + s.filteredAttributes.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Exemplar) computeDiff(val *Exemplar) (ret bool) { @@ -269,21 +285,18 @@ func (s *Exemplar) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Exemplar) cloneShared(allocators *Allocators) Exemplar { - return s.Clone(allocators) -} - -func (s *Exemplar) Clone(allocators *Allocators) Exemplar { - c := Exemplar{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Exemplar) CloneTo(dst *Exemplar, allocators *Allocators) { + *dst = Exemplar{ timestamp: s.timestamp, spanID: s.spanID, traceID: s.traceID, } - copyToNewExemplarValue(&c.value, &s.value, allocators) - copyToNewAttributes(&c.filteredAttributes, &s.filteredAttributes, allocators) - return c + copyToNewExemplarValue(&dst.value, &s.value, allocators) + copyToNewAttributes(&dst.filteredAttributes, &s.filteredAttributes, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/exemplararray.go b/go/otel/otelstef/exemplararray.go index 7750b267..d5d6103e 100644 --- a/go/otel/otelstef/exemplararray.go +++ b/go/otel/otelstef/exemplararray.go @@ -52,6 +52,15 @@ func (e *ExemplarArray) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *ExemplarArray) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit + for i := range e.elems { + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + } +} + func (e *ExemplarArray) canBeShared() bool { // An array can never be shared. return false @@ -96,6 +105,14 @@ func (e *ExemplarArray) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *ExemplarArray) clearModifiedRecursively() { + for i := 0; i < len(e.elems); i++ { + e.elems[i].clearModifiedRecursively() + } + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *ExemplarArray) computeDiff(val *ExemplarArray) (ret bool) { diff --git a/go/otel/otelstef/exemplarvalue.go b/go/otel/otelstef/exemplarvalue.go index ff9d6c87..11e9dab1 100644 --- a/go/otel/otelstef/exemplarvalue.go +++ b/go/otel/otelstef/exemplarvalue.go @@ -73,6 +73,15 @@ func (s *ExemplarValue) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointer and bit in a newly copied oneof. +func (s *ExemplarValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.parentModifiedFields = parentModifiedFields + s.parentModifiedBit = parentModifiedBit + + switch s.Type() { + } +} + type ExemplarValueType byte const ( @@ -159,21 +168,19 @@ func (s *ExemplarValue) canBeShared() bool { return false } -func (s *ExemplarValue) cloneShared(allocators *Allocators) ExemplarValue { - // Oneof is not shareable, so cloneShared is just a Clone. - return s.Clone(allocators) -} - -func (s *ExemplarValue) Clone(allocators *Allocators) ExemplarValue { - c := ExemplarValue{} - c.clearValSetType(s.Type()) +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *ExemplarValue) CloneTo(dst *ExemplarValue, allocators *Allocators) { + *dst = ExemplarValue{} + dst.clearValSetType(s.Type()) switch s.Type() { case ExemplarValueTypeInt64: - *c.int64Ptr() = *s.int64Ptr() + *dst.int64Ptr() = *s.int64Ptr() case ExemplarValueTypeFloat64: - *c.float64Ptr() = *s.float64Ptr() + *dst.float64Ptr() = *s.float64Ptr() } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -241,6 +248,12 @@ func (s *ExemplarValue) setUnmodifiedRecursively() { } } +// clearModifiedRecursively clears modification state in all mutable descendants. +func (s *ExemplarValue) clearModifiedRecursively() { + switch s.Type() { + } +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *ExemplarValue) computeDiff(val *ExemplarValue) (ret bool) { diff --git a/go/otel/otelstef/exphistogrambuckets.go b/go/otel/otelstef/exphistogrambuckets.go index dc06b7a6..b4eb297a 100644 --- a/go/otel/otelstef/exphistogrambuckets.go +++ b/go/otel/otelstef/exphistogrambuckets.go @@ -83,6 +83,13 @@ func (s *ExpHistogramBuckets) fixParent(parentModifiedFields *modifiedFields) { s.bucketCounts.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *ExpHistogramBuckets) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.bucketCounts.attachParent(&s.modifiedFields, fieldModifiedExpHistogramBucketsBucketCounts) +} + func (s *ExpHistogramBuckets) freeze() { if s.isFrozen() { return @@ -149,6 +156,13 @@ func (s *ExpHistogramBuckets) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *ExpHistogramBuckets) clearModifiedRecursively() { + s.bucketCounts.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *ExpHistogramBuckets) computeDiff(val *ExpHistogramBuckets) (ret bool) { @@ -170,18 +184,15 @@ func (s *ExpHistogramBuckets) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *ExpHistogramBuckets) cloneShared(allocators *Allocators) ExpHistogramBuckets { - return s.Clone(allocators) -} - -func (s *ExpHistogramBuckets) Clone(allocators *Allocators) ExpHistogramBuckets { - c := ExpHistogramBuckets{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *ExpHistogramBuckets) CloneTo(dst *ExpHistogramBuckets, allocators *Allocators) { + *dst = ExpHistogramBuckets{ offset: s.offset, } - copyToNewUint64Array(&c.bucketCounts, &s.bucketCounts, allocators) - return c + copyToNewUint64Array(&dst.bucketCounts, &s.bucketCounts, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/exphistogramvalue.go b/go/otel/otelstef/exphistogramvalue.go index 73151ea8..fabcb785 100644 --- a/go/otel/otelstef/exphistogramvalue.go +++ b/go/otel/otelstef/exphistogramvalue.go @@ -119,6 +119,14 @@ func (s *ExpHistogramValue) fixParent(parentModifiedFields *modifiedFields) { s.negativeBuckets.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *ExpHistogramValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.positiveBuckets.attachParent(&s.modifiedFields, fieldModifiedExpHistogramValuePositiveBuckets) + s.negativeBuckets.attachParent(&s.modifiedFields, fieldModifiedExpHistogramValueNegativeBuckets) +} + func (s *ExpHistogramValue) freeze() { if s.isFrozen() { return @@ -399,6 +407,14 @@ func (s *ExpHistogramValue) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *ExpHistogramValue) clearModifiedRecursively() { + s.positiveBuckets.clearModifiedRecursively() + s.negativeBuckets.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *ExpHistogramValue) computeDiff(val *ExpHistogramValue) (ret bool) { @@ -497,14 +513,10 @@ func (s *ExpHistogramValue) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *ExpHistogramValue) cloneShared(allocators *Allocators) ExpHistogramValue { - return s.Clone(allocators) -} - -func (s *ExpHistogramValue) Clone(allocators *Allocators) ExpHistogramValue { - c := ExpHistogramValue{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *ExpHistogramValue) CloneTo(dst *ExpHistogramValue, allocators *Allocators) { + *dst = ExpHistogramValue{ count: s.count, sum: s.sum, min: s.min, @@ -513,9 +525,11 @@ func (s *ExpHistogramValue) Clone(allocators *Allocators) ExpHistogramValue { zeroCount: s.zeroCount, zeroThreshold: s.zeroThreshold, } - copyToNewExpHistogramBuckets(&c.positiveBuckets, &s.positiveBuckets, allocators) - copyToNewExpHistogramBuckets(&c.negativeBuckets, &s.negativeBuckets, allocators) - return c + copyToNewExpHistogramBuckets(&dst.positiveBuckets, &s.positiveBuckets, allocators) + copyToNewExpHistogramBuckets(&dst.negativeBuckets, &s.negativeBuckets, allocators) + dst.optionalFieldsPresent = s.optionalFieldsPresent + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/float64array.go b/go/otel/otelstef/float64array.go index 5d03bd1c..8b94ac10 100644 --- a/go/otel/otelstef/float64array.go +++ b/go/otel/otelstef/float64array.go @@ -51,6 +51,12 @@ func (e *Float64Array) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *Float64Array) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit +} + func (e *Float64Array) canBeShared() bool { // An array can never be shared. return false @@ -97,6 +103,11 @@ func (e *Float64Array) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *Float64Array) clearModifiedRecursively() { + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *Float64Array) computeDiff(val *Float64Array) (ret bool) { diff --git a/go/otel/otelstef/histogramvalue.go b/go/otel/otelstef/histogramvalue.go index 18a9f9a1..f14534cc 100644 --- a/go/otel/otelstef/histogramvalue.go +++ b/go/otel/otelstef/histogramvalue.go @@ -104,6 +104,13 @@ func (s *HistogramValue) fixParent(parentModifiedFields *modifiedFields) { s.bucketCounts.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *HistogramValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.bucketCounts.attachParent(&s.modifiedFields, fieldModifiedHistogramValueBucketCounts) +} + func (s *HistogramValue) freeze() { if s.isFrozen() { return @@ -287,6 +294,13 @@ func (s *HistogramValue) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *HistogramValue) clearModifiedRecursively() { + s.bucketCounts.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *HistogramValue) computeDiff(val *HistogramValue) (ret bool) { @@ -365,21 +379,19 @@ func (s *HistogramValue) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *HistogramValue) cloneShared(allocators *Allocators) HistogramValue { - return s.Clone(allocators) -} - -func (s *HistogramValue) Clone(allocators *Allocators) HistogramValue { - c := HistogramValue{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *HistogramValue) CloneTo(dst *HistogramValue, allocators *Allocators) { + *dst = HistogramValue{ count: s.count, sum: s.sum, min: s.min, max: s.max, } - copyToNewUint64Array(&c.bucketCounts, &s.bucketCounts, allocators) - return c + copyToNewUint64Array(&dst.bucketCounts, &s.bucketCounts, allocators) + dst.optionalFieldsPresent = s.optionalFieldsPresent + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/keyvaluelist.go b/go/otel/otelstef/keyvaluelist.go index 05c8523b..114adaea 100644 --- a/go/otel/otelstef/keyvaluelist.go +++ b/go/otel/otelstef/keyvaluelist.go @@ -60,6 +60,15 @@ func (m *KeyValueList) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointers and bits in a newly copied multimap. +func (m *KeyValueList) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.modifiedElems.attachParent(parentModifiedFields, parentModifiedBit) + elems := m.elems[:m.initedCount] + for i := 0; i < len(elems); i++ { + elems[i].value.attachParent(&m.modifiedElems.vals, m.modifiedElems.maskForIndex(i)) + } +} + func (m *KeyValueList) canBeShared() bool { // Multimap can never be shared. return false @@ -122,6 +131,14 @@ func (m *KeyValueList) setUnmodifiedRecursively() { m.modifiedElems.setUnmodifiedAll() } +// clearModifiedRecursively clears modification state in all mutable elements. +func (m *KeyValueList) clearModifiedRecursively() { + for i := 0; i < len(m.elems); i++ { + m.elems[i].value.clearModifiedRecursively() + } + m.modifiedElems.setUnmodifiedAll() +} + // computeDiff compares m to val and returns true if they differ. // All fields that are different in m will be marked as modified. func (m *KeyValueList) computeDiff(val *KeyValueList) (ret bool) { diff --git a/go/otel/otelstef/link.go b/go/otel/otelstef/link.go index beffd4f8..b4fd391a 100644 --- a/go/otel/otelstef/link.go +++ b/go/otel/otelstef/link.go @@ -95,6 +95,13 @@ func (s *Link) fixParent(parentModifiedFields *modifiedFields) { s.attributes.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Link) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.attributes.attachParent(&s.modifiedFields, fieldModifiedLinkAttributes) +} + func (s *Link) freeze() { if s.isFrozen() { return @@ -261,6 +268,13 @@ func (s *Link) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Link) clearModifiedRecursively() { + s.attributes.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Link) computeDiff(val *Link) (ret bool) { @@ -302,22 +316,19 @@ func (s *Link) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Link) cloneShared(allocators *Allocators) Link { - return s.Clone(allocators) -} - -func (s *Link) Clone(allocators *Allocators) Link { - c := Link{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Link) CloneTo(dst *Link, allocators *Allocators) { + *dst = Link{ traceID: s.traceID, spanID: s.spanID, traceState: s.traceState, flags: s.flags, droppedAttributesCount: s.droppedAttributesCount, } - copyToNewAttributes(&c.attributes, &s.attributes, allocators) - return c + copyToNewAttributes(&dst.attributes, &s.attributes, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/linkarray.go b/go/otel/otelstef/linkarray.go index a12698b8..d12f1bdb 100644 --- a/go/otel/otelstef/linkarray.go +++ b/go/otel/otelstef/linkarray.go @@ -52,6 +52,15 @@ func (e *LinkArray) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *LinkArray) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit + for i := range e.elems { + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + } +} + func (e *LinkArray) canBeShared() bool { // An array can never be shared. return false @@ -96,6 +105,14 @@ func (e *LinkArray) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *LinkArray) clearModifiedRecursively() { + for i := 0; i < len(e.elems); i++ { + e.elems[i].clearModifiedRecursively() + } + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *LinkArray) computeDiff(val *LinkArray) (ret bool) { diff --git a/go/otel/otelstef/logrecord.go b/go/otel/otelstef/logrecord.go new file mode 100644 index 00000000..65448328 --- /dev/null +++ b/go/otel/otelstef/logrecord.go @@ -0,0 +1,1541 @@ +// Code generated by stefc. DO NOT EDIT. +package otelstef + +import ( + "bytes" + "fmt" + "math/rand/v2" + "strings" + "unsafe" + + "github.com/splunk/stef/go/pkg" + "github.com/splunk/stef/go/pkg/codecs" + "github.com/splunk/stef/go/pkg/schema" +) + +var _ = strings.Compare +var _ = codecs.StringEncoder{} +var _ = schema.WireSchema{} +var _ = bytes.NewBuffer + +type LogRecord struct { + timeUnixNano uint64 + observedTimeUnixNano uint64 + severityNumber uint64 + severityText string + body *AnyValue + attributes Attributes + droppedAttributesCount uint64 + flags uint64 + traceID pkg.Bytes + spanID pkg.Bytes + eventName string + + // modifiedFields keeps track of which fields are modified. + modifiedFields modifiedFields +} + +const LogRecordStructName = "LogRecord" + +// Bitmasks for "modified" flags for each field. +const ( + fieldModifiedLogRecordTimeUnixNano = uint64(1 << iota) + fieldModifiedLogRecordObservedTimeUnixNano + fieldModifiedLogRecordSeverityNumber + fieldModifiedLogRecordSeverityText + fieldModifiedLogRecordBody + fieldModifiedLogRecordAttributes + fieldModifiedLogRecordDroppedAttributesCount + fieldModifiedLogRecordFlags + fieldModifiedLogRecordTraceID + fieldModifiedLogRecordSpanID + fieldModifiedLogRecordEventName +) + +// Init must be called once, before the LogRecord is used. +func (s *LogRecord) Init() { + s.init(nil, 0) +} + +func NewLogRecord() *LogRecord { + var s LogRecord + s.init(nil, 0) + return &s +} + +func (s *LogRecord) init(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + + s.body = &AnyValue{} + s.body.init(&s.modifiedFields, fieldModifiedLogRecordBody) + s.attributes.init(&s.modifiedFields, fieldModifiedLogRecordAttributes) +} + +func (s *LogRecord) initAlloc(parentModifiedFields *modifiedFields, parentModifiedBit uint64, allocators *Allocators) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(AnyValue{}))) + s.body = allocators.AnyValue.Alloc() + s.body.initAlloc(&s.modifiedFields, fieldModifiedLogRecordBody, allocators) + s.attributes.initAlloc(&s.modifiedFields, fieldModifiedLogRecordAttributes, allocators) +} + +// Reset the struct to its initial state. +func (s *LogRecord) Reset() { + s.reset() + s.setUnmodifiedRecursively() +} + +// reset the struct to its initial state, as if init() was just called. +// Will not reset internal fields such as parentModifiedFields. +func (s *LogRecord) reset() { + if s.modifiedFields.isFrozen() { + panic("cannot modify frozen LogRecord") + } + s.timeUnixNano = 0 + s.observedTimeUnixNano = 0 + s.severityNumber = 0 + s.severityText = "" + if s.body != nil { + s.body.reset() + } + s.attributes.reset() + s.droppedAttributesCount = 0 + s.flags = 0 + s.traceID = pkg.EmptyBytes + s.spanID = pkg.EmptyBytes + s.eventName = "" +} + +// fixParent sets the parentModifiedFields pointer to the supplied value. +// This is used when the parent is moved in memory for example because the parent +// an array element and the array was expanded. +func (s *LogRecord) fixParent(parentModifiedFields *modifiedFields) { + s.modifiedFields.parent = parentModifiedFields + s.body.fixParent(&s.modifiedFields) + s.attributes.fixParent(&s.modifiedFields) +} + +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *LogRecord) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.body.attachParent(&s.modifiedFields, fieldModifiedLogRecordBody) + s.attributes.attachParent(&s.modifiedFields, fieldModifiedLogRecordAttributes) +} + +func (s *LogRecord) freeze() { + if s.isFrozen() { + return + } + s.modifiedFields.freeze() + s.body.freeze() + s.attributes.freeze() +} + +func (s *LogRecord) isFrozen() bool { + return s.modifiedFields.isFrozen() +} + +func (s *LogRecord) TimeUnixNano() uint64 { + return s.timeUnixNano +} + +// SetTimeUnixNano sets the value of TimeUnixNano field. +func (s *LogRecord) SetTimeUnixNano(v uint64) { + if s.timeUnixNano != v { + s.timeUnixNano = v + s.modifiedFields.markModified(fieldModifiedLogRecordTimeUnixNano) + } +} + +func (s *LogRecord) markTimeUnixNanoModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordTimeUnixNano) +} + +// IsTimeUnixNanoModified returns true the value of TimeUnixNano field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsTimeUnixNanoModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordTimeUnixNano != 0 +} + +func (s *LogRecord) ObservedTimeUnixNano() uint64 { + return s.observedTimeUnixNano +} + +// SetObservedTimeUnixNano sets the value of ObservedTimeUnixNano field. +func (s *LogRecord) SetObservedTimeUnixNano(v uint64) { + if s.observedTimeUnixNano != v { + s.observedTimeUnixNano = v + s.modifiedFields.markModified(fieldModifiedLogRecordObservedTimeUnixNano) + } +} + +func (s *LogRecord) markObservedTimeUnixNanoModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordObservedTimeUnixNano) +} + +// IsObservedTimeUnixNanoModified returns true the value of ObservedTimeUnixNano field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsObservedTimeUnixNanoModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordObservedTimeUnixNano != 0 +} + +func (s *LogRecord) SeverityNumber() SeverityNumber { + return SeverityNumber(s.severityNumber) +} + +// SetSeverityNumber sets the value of SeverityNumber field. +func (s *LogRecord) SetSeverityNumber(v SeverityNumber) { + if s.severityNumber != uint64(v) { + s.severityNumber = uint64(v) + s.modifiedFields.markModified(fieldModifiedLogRecordSeverityNumber) + } +} + +func (s *LogRecord) markSeverityNumberModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordSeverityNumber) +} + +// IsSeverityNumberModified returns true the value of SeverityNumber field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsSeverityNumberModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordSeverityNumber != 0 +} + +func (s *LogRecord) SeverityText() string { + return s.severityText +} + +// SetSeverityText sets the value of SeverityText field. +func (s *LogRecord) SetSeverityText(v string) { + if s.severityText != v { + s.severityText = v + s.modifiedFields.markModified(fieldModifiedLogRecordSeverityText) + } +} + +func (s *LogRecord) markSeverityTextModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordSeverityText) +} + +// IsSeverityTextModified returns true the value of SeverityText field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsSeverityTextModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordSeverityText != 0 +} + +func (s *LogRecord) Body() *AnyValue { + return s.body +} + +func (s *LogRecord) markBodyModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordBody) +} + +// IsBodyModified returns true the value of Body field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsBodyModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordBody != 0 +} + +func (s *LogRecord) Attributes() *Attributes { + return &s.attributes +} + +func (s *LogRecord) markAttributesModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordAttributes) +} + +// IsAttributesModified returns true the value of Attributes field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsAttributesModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordAttributes != 0 +} + +func (s *LogRecord) DroppedAttributesCount() uint64 { + return s.droppedAttributesCount +} + +// SetDroppedAttributesCount sets the value of DroppedAttributesCount field. +func (s *LogRecord) SetDroppedAttributesCount(v uint64) { + if s.droppedAttributesCount != v { + s.droppedAttributesCount = v + s.modifiedFields.markModified(fieldModifiedLogRecordDroppedAttributesCount) + } +} + +func (s *LogRecord) markDroppedAttributesCountModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordDroppedAttributesCount) +} + +// IsDroppedAttributesCountModified returns true the value of DroppedAttributesCount field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsDroppedAttributesCountModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordDroppedAttributesCount != 0 +} + +func (s *LogRecord) Flags() uint64 { + return s.flags +} + +// SetFlags sets the value of Flags field. +func (s *LogRecord) SetFlags(v uint64) { + if s.flags != v { + s.flags = v + s.modifiedFields.markModified(fieldModifiedLogRecordFlags) + } +} + +func (s *LogRecord) markFlagsModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordFlags) +} + +// IsFlagsModified returns true the value of Flags field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsFlagsModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordFlags != 0 +} + +func (s *LogRecord) TraceID() pkg.Bytes { + return s.traceID +} + +// SetTraceID sets the value of TraceID field. +func (s *LogRecord) SetTraceID(v pkg.Bytes) { + if s.traceID != v { + s.traceID = v + s.modifiedFields.markModified(fieldModifiedLogRecordTraceID) + } +} + +func (s *LogRecord) markTraceIDModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordTraceID) +} + +// IsTraceIDModified returns true the value of TraceID field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsTraceIDModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordTraceID != 0 +} + +func (s *LogRecord) SpanID() pkg.Bytes { + return s.spanID +} + +// SetSpanID sets the value of SpanID field. +func (s *LogRecord) SetSpanID(v pkg.Bytes) { + if s.spanID != v { + s.spanID = v + s.modifiedFields.markModified(fieldModifiedLogRecordSpanID) + } +} + +func (s *LogRecord) markSpanIDModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordSpanID) +} + +// IsSpanIDModified returns true the value of SpanID field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsSpanIDModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordSpanID != 0 +} + +func (s *LogRecord) EventName() string { + return s.eventName +} + +// SetEventName sets the value of EventName field. +func (s *LogRecord) SetEventName(v string) { + if s.eventName != v { + s.eventName = v + s.modifiedFields.markModified(fieldModifiedLogRecordEventName) + } +} + +func (s *LogRecord) markEventNameModified() { + s.modifiedFields.markModified(fieldModifiedLogRecordEventName) +} + +// IsEventNameModified returns true the value of EventName field was modified since +// LogRecord was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *LogRecord) IsEventNameModified() bool { + return s.modifiedFields.mask&fieldModifiedLogRecordEventName != 0 +} + +func (s *LogRecord) setModifiedRecursively() { + s.body.setModifiedRecursively() + s.attributes.setModifiedRecursively() + s.modifiedFields.mask = + fieldModifiedLogRecordTimeUnixNano | + fieldModifiedLogRecordObservedTimeUnixNano | + fieldModifiedLogRecordSeverityNumber | + fieldModifiedLogRecordSeverityText | + fieldModifiedLogRecordBody | + fieldModifiedLogRecordAttributes | + fieldModifiedLogRecordDroppedAttributesCount | + fieldModifiedLogRecordFlags | + fieldModifiedLogRecordTraceID | + fieldModifiedLogRecordSpanID | + fieldModifiedLogRecordEventName | 0 +} + +func (s *LogRecord) setUnmodifiedRecursively() { + if s.IsBodyModified() { + s.body.setUnmodifiedRecursively() + } + if s.IsAttributesModified() { + s.attributes.setUnmodifiedRecursively() + } + s.modifiedFields.mask = 0 +} + +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *LogRecord) clearModifiedRecursively() { + s.body.clearModifiedRecursively() + s.attributes.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + +// computeDiff compares s and val and returns true if they differ. +// All fields that are different in s will be marked as modified. +func (s *LogRecord) computeDiff(val *LogRecord) (ret bool) { + // Compare TimeUnixNano field. + if s.timeUnixNano != val.timeUnixNano { + s.modifiedFields.setModified(fieldModifiedLogRecordTimeUnixNano) + ret = true + } + // Compare ObservedTimeUnixNano field. + if s.observedTimeUnixNano != val.observedTimeUnixNano { + s.modifiedFields.setModified(fieldModifiedLogRecordObservedTimeUnixNano) + ret = true + } + // Compare SeverityNumber field. + if s.severityNumber != val.severityNumber { + s.modifiedFields.setModified(fieldModifiedLogRecordSeverityNumber) + ret = true + } + // Compare SeverityText field. + if s.severityText != val.severityText { + s.modifiedFields.setModified(fieldModifiedLogRecordSeverityText) + ret = true + } + // Compare Body field. + if s.body.computeDiff(val.body) { + s.modifiedFields.setModified(fieldModifiedLogRecordBody) + ret = true + } + // Compare Attributes field. + if s.attributes.computeDiff(&val.attributes) { + s.modifiedFields.setModified(fieldModifiedLogRecordAttributes) + ret = true + } + // Compare DroppedAttributesCount field. + if s.droppedAttributesCount != val.droppedAttributesCount { + s.modifiedFields.setModified(fieldModifiedLogRecordDroppedAttributesCount) + ret = true + } + // Compare Flags field. + if s.flags != val.flags { + s.modifiedFields.setModified(fieldModifiedLogRecordFlags) + ret = true + } + // Compare TraceID field. + if s.traceID != val.traceID { + s.modifiedFields.setModified(fieldModifiedLogRecordTraceID) + ret = true + } + // Compare SpanID field. + if s.spanID != val.spanID { + s.modifiedFields.setModified(fieldModifiedLogRecordSpanID) + ret = true + } + // Compare EventName field. + if s.eventName != val.eventName { + s.modifiedFields.setModified(fieldModifiedLogRecordEventName) + ret = true + } + return ret +} + +// canBeShared returns true if s is safe to share by pointer without cloning (for example if s is frozen). +func (s *LogRecord) canBeShared() bool { + return false +} + +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *LogRecord) CloneTo(dst *LogRecord, allocators *Allocators) { + *dst = LogRecord{ + timeUnixNano: s.timeUnixNano, + observedTimeUnixNano: s.observedTimeUnixNano, + severityNumber: s.severityNumber, + severityText: s.severityText, + droppedAttributesCount: s.droppedAttributesCount, + flags: s.flags, + traceID: s.traceID, + spanID: s.spanID, + eventName: s.eventName, + } + dst.body = s.body.cloneShared(allocators) + copyToNewAttributes(&dst.attributes, &s.attributes, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() +} + +// ByteSize returns approximate memory usage in bytes. Used to calculate +// memory used by dictionaries. +func (s *LogRecord) byteSize() uint { + return uint(unsafe.Sizeof(*s)) + + s.body.byteSize() + s.attributes.byteSize() + 0 +} + +// Copy from src to dst, overwriting existing data in dst. +func copyLogRecord(dst *LogRecord, src *LogRecord) { + dst.SetTimeUnixNano(src.timeUnixNano) + dst.SetObservedTimeUnixNano(src.observedTimeUnixNano) + dst.SetSeverityNumber(SeverityNumber(src.severityNumber)) + dst.SetSeverityText(src.severityText) + + if src.body.canBeShared() { + if src.body.computeDiff(dst.body) { + dst.body = src.body + dst.markBodyModified() + } + } else { + if dst.body == nil || dst.body.canBeShared() { // Not allowed to modify shared data. + dst.body = new(AnyValue) + dst.body.init(&dst.modifiedFields, fieldModifiedLogRecordBody) + } + copyAnyValue(dst.body, src.body) + } + copyAttributes(&dst.attributes, &src.attributes) + dst.SetDroppedAttributesCount(src.droppedAttributesCount) + dst.SetFlags(src.flags) + dst.SetTraceID(src.traceID) + dst.SetSpanID(src.spanID) + dst.SetEventName(src.eventName) +} + +// Copy from src to dst. dst is assumed to be just inited. +func copyToNewLogRecord(dst *LogRecord, src *LogRecord, allocators *Allocators) { + dst.SetTimeUnixNano(src.timeUnixNano) + dst.SetObservedTimeUnixNano(src.observedTimeUnixNano) + dst.SetSeverityNumber(SeverityNumber(src.severityNumber)) + dst.SetSeverityText(src.severityText) + + if src.body.canBeShared() { + dst.body = src.body + } else { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(AnyValue{}))) + dst.body = allocators.AnyValue.Alloc() + dst.body.init(&dst.modifiedFields, fieldModifiedLogRecordBody) + copyToNewAnyValue(dst.body, src.body, allocators) + } + + copyToNewAttributes(&dst.attributes, &src.attributes, allocators) + dst.SetDroppedAttributesCount(src.droppedAttributesCount) + dst.SetFlags(src.flags) + dst.SetTraceID(src.traceID) + dst.SetSpanID(src.spanID) + dst.SetEventName(src.eventName) +} + +// CopyFrom() performs a deep copy from src. +func (s *LogRecord) CopyFrom(src *LogRecord) { + copyLogRecord(s, src) +} + +// mutateRandom mutates fields in a random, deterministic manner using +// random parameter as a deterministic generator. Only fields that exist +// in the schem are mutated, allowing to generate data for specified schema. +// The state of "modified" flags is undefined after this operation. +func (s *LogRecord) mutateRandom(random *rand.Rand, schem *schema.Schema, limiter *mutateRandomLimiter) { + // Get the field count for this struct from the schema. If the schema specifies + // fewer field count than the one we have in this code then we will not mutate + // fields that are not in the schema. + fieldCount, err := schem.FieldCount("LogRecord") + if err != nil { + panic(fmt.Sprintf("cannot get field count for %s: %v", "LogRecord", err)) + } + + const randRange = max(11, 2) // At least 2 to ensure we don't recurse infinitely if there is only 1 field. + + if fieldCount <= 0 { + return // TimeUnixNano and all subsequent fields are skipped. + } + // Maybe mutate TimeUnixNano + if random.IntN(randRange) == 0 { + s.SetTimeUnixNano(pkg.Uint64Random(random)) + } + if fieldCount <= 1 { + return // ObservedTimeUnixNano and all subsequent fields are skipped. + } + // Maybe mutate ObservedTimeUnixNano + if random.IntN(randRange) == 0 { + s.SetObservedTimeUnixNano(pkg.Uint64Random(random)) + } + if fieldCount <= 2 { + return // SeverityNumber and all subsequent fields are skipped. + } + // Maybe mutate SeverityNumber + if random.IntN(randRange) == 0 { + s.SetSeverityNumber(SeverityNumber(pkg.Uint64Random(random) % 25)) + } + if fieldCount <= 3 { + return // SeverityText and all subsequent fields are skipped. + } + // Maybe mutate SeverityText + if random.IntN(randRange) == 0 { + s.SetSeverityText(pkg.StringRandom(random)) + } + if fieldCount <= 4 { + return // Body and all subsequent fields are skipped. + } + // Maybe mutate Body + if random.IntN(randRange) == 0 { + s.body.mutateRandom(random, schem, limiter) + } + if fieldCount <= 5 { + return // Attributes and all subsequent fields are skipped. + } + // Maybe mutate Attributes + if random.IntN(randRange) == 0 { + s.attributes.mutateRandom(random, schem, limiter) + } + if fieldCount <= 6 { + return // DroppedAttributesCount and all subsequent fields are skipped. + } + // Maybe mutate DroppedAttributesCount + if random.IntN(randRange) == 0 { + s.SetDroppedAttributesCount(pkg.Uint64Random(random)) + } + if fieldCount <= 7 { + return // Flags and all subsequent fields are skipped. + } + // Maybe mutate Flags + if random.IntN(randRange) == 0 { + s.SetFlags(pkg.Uint64Random(random)) + } + if fieldCount <= 8 { + return // TraceID and all subsequent fields are skipped. + } + // Maybe mutate TraceID + if random.IntN(randRange) == 0 { + s.SetTraceID(pkg.BytesRandom(random)) + } + if fieldCount <= 9 { + return // SpanID and all subsequent fields are skipped. + } + // Maybe mutate SpanID + if random.IntN(randRange) == 0 { + s.SetSpanID(pkg.BytesRandom(random)) + } + if fieldCount <= 10 { + return // EventName and all subsequent fields are skipped. + } + // Maybe mutate EventName + if random.IntN(randRange) == 0 { + s.SetEventName(pkg.StringRandom(random)) + } +} + +// IsEqual performs deep comparison and returns true if struct is equal to right. +func (s *LogRecord) IsEqual(right *LogRecord) bool { + // Compare TimeUnixNano field. + if !pkg.Uint64Equal(s.timeUnixNano, right.timeUnixNano) { + return false + } + // Compare ObservedTimeUnixNano field. + if !pkg.Uint64Equal(s.observedTimeUnixNano, right.observedTimeUnixNano) { + return false + } + // Compare SeverityNumber field. + if !pkg.Uint64Equal(s.severityNumber, right.severityNumber) { + return false + } + // Compare SeverityText field. + if !pkg.StringEqual(s.severityText, right.severityText) { + return false + } + // Compare Body field. + if !s.body.IsEqual(right.body) { + return false + } + // Compare Attributes field. + if !s.attributes.IsEqual(&right.attributes) { + return false + } + // Compare DroppedAttributesCount field. + if !pkg.Uint64Equal(s.droppedAttributesCount, right.droppedAttributesCount) { + return false + } + // Compare Flags field. + if !pkg.Uint64Equal(s.flags, right.flags) { + return false + } + // Compare TraceID field. + if !pkg.BytesEqual(s.traceID, right.traceID) { + return false + } + // Compare SpanID field. + if !pkg.BytesEqual(s.spanID, right.spanID) { + return false + } + // Compare EventName field. + if !pkg.StringEqual(s.eventName, right.eventName) { + return false + } + + return true +} + +// CmpLogRecord performs deep comparison and returns an integer that +// will be 0 if left == right, negative if left < right, positive if left > right. +func CmpLogRecord(left, right *LogRecord) int { + // Compare TimeUnixNano field. + if c := pkg.Uint64Compare(left.timeUnixNano, right.timeUnixNano); c != 0 { + return c + } + // Compare ObservedTimeUnixNano field. + if c := pkg.Uint64Compare(left.observedTimeUnixNano, right.observedTimeUnixNano); c != 0 { + return c + } + // Compare SeverityNumber field. + if c := pkg.Uint64Compare(left.severityNumber, right.severityNumber); c != 0 { + return c + } + // Compare SeverityText field. + if c := strings.Compare(left.severityText, right.severityText); c != 0 { + return c + } + // Compare Body field. + if c := CmpAnyValue(left.body, right.body); c != 0 { + return c + } + // Compare Attributes field. + if c := CmpAttributes(&left.attributes, &right.attributes); c != 0 { + return c + } + // Compare DroppedAttributesCount field. + if c := pkg.Uint64Compare(left.droppedAttributesCount, right.droppedAttributesCount); c != 0 { + return c + } + // Compare Flags field. + if c := pkg.Uint64Compare(left.flags, right.flags); c != 0 { + return c + } + // Compare TraceID field. + if c := pkg.BytesCompare(left.traceID, right.traceID); c != 0 { + return c + } + // Compare SpanID field. + if c := pkg.BytesCompare(left.spanID, right.spanID); c != 0 { + return c + } + // Compare EventName field. + if c := strings.Compare(left.eventName, right.eventName); c != 0 { + return c + } + return 0 +} + +// LogRecordEncoder implements encoding of LogRecord +type LogRecordEncoder struct { + buf pkg.BitsWriter + limiter *pkg.SizeLimiter + + // forceModifiedFields is set to a mask to force the next encoding operation + // write the fields, whether they are modified or no. This is used after frame + // restarts so that the data can be decoded from the frame start. + forceModifiedFields uint64 + + timeUnixNanoEncoder codecs.Uint64Encoder + observedTimeUnixNanoEncoder codecs.Uint64Encoder + severityNumberEncoder codecs.Uint64Encoder + severityTextEncoder codecs.StringDictEncoder + bodyEncoder *AnyValueEncoder + isBodyRecursive bool // Indicates Body field's type is recursive. + attributesEncoder *AttributesEncoder + isAttributesRecursive bool // Indicates Attributes field's type is recursive. + droppedAttributesCountEncoder codecs.Uint64Encoder + flagsEncoder codecs.Uint64Encoder + traceIDEncoder codecs.BytesEncoder + spanIDEncoder codecs.BytesEncoder + eventNameEncoder codecs.StringDictEncoder + + allocators *Allocators + + keepFieldMask uint64 + fieldCount uint +} + +func (e *LogRecordEncoder) Init(state *WriterState, columns *pkg.WriteColumnSet) error { + // Remember this encoder in the state so that we can detect recursion. + if state.LogRecordEncoder != nil { + panic("cannot initialize LogRecordEncoder: already initialized") + } + state.LogRecordEncoder = e + defer func() { state.LogRecordEncoder = nil }() + + e.limiter = &state.limiter + e.allocators = &state.Allocators + + // Number of fields in the output data schema. + var err error + e.fieldCount, err = state.StructFieldCounts.LogRecordFieldCount() + if err != nil { + return fmt.Errorf("cannot find struct %s in override schema: %w", "LogRecord", err) + } + // Set that many 1 bits in the keepFieldMask. All fields with higher number + // will be skipped when encoding. + e.keepFieldMask = ^(^uint64(0) << e.fieldCount) + + // Init encoder for TimeUnixNano field. + if e.fieldCount <= 0 { + return nil // TimeUnixNano and all subsequent fields are skipped. + } + err = e.timeUnixNanoEncoder.Init(e.limiter, columns.AddSubColumn()) + if err != nil { + return err + } + + // Init encoder for ObservedTimeUnixNano field. + if e.fieldCount <= 1 { + return nil // ObservedTimeUnixNano and all subsequent fields are skipped. + } + err = e.observedTimeUnixNanoEncoder.Init(e.limiter, columns.AddSubColumn()) + if err != nil { + return err + } + + // Init encoder for SeverityNumber field. + if e.fieldCount <= 2 { + return nil // SeverityNumber and all subsequent fields are skipped. + } + err = e.severityNumberEncoder.Init(e.limiter, columns.AddSubColumn()) + if err != nil { + return err + } + + // Init encoder for SeverityText field. + if e.fieldCount <= 3 { + return nil // SeverityText and all subsequent fields are skipped. + } + err = e.severityTextEncoder.Init(&state.LogSeverityText, e.limiter, columns.AddSubColumn()) + if err != nil { + return err + } + + // Init encoder for Body field. + if e.fieldCount <= 4 { + return nil // Body and all subsequent fields are skipped. + } + if state.AnyValueEncoder != nil { + // Recursion detected, use the existing encoder. + e.bodyEncoder = state.AnyValueEncoder + e.isBodyRecursive = true + } else { + e.bodyEncoder = new(AnyValueEncoder) + err = e.bodyEncoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + + // Init encoder for Attributes field. + if e.fieldCount <= 5 { + return nil // Attributes and all subsequent fields are skipped. + } + if state.AttributesEncoder != nil { + // Recursion detected, use the existing encoder. + e.attributesEncoder = state.AttributesEncoder + e.isAttributesRecursive = true + } else { + e.attributesEncoder = new(AttributesEncoder) + err = e.attributesEncoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + + // Init encoder for DroppedAttributesCount field. + if e.fieldCount <= 6 { + return nil // DroppedAttributesCount and all subsequent fields are skipped. + } + err = e.droppedAttributesCountEncoder.Init(e.limiter, columns.AddSubColumn()) + if err != nil { + return err + } + + // Init encoder for Flags field. + if e.fieldCount <= 7 { + return nil // Flags and all subsequent fields are skipped. + } + err = e.flagsEncoder.Init(e.limiter, columns.AddSubColumn()) + if err != nil { + return err + } + + // Init encoder for TraceID field. + if e.fieldCount <= 8 { + return nil // TraceID and all subsequent fields are skipped. + } + err = e.traceIDEncoder.Init(e.limiter, columns.AddSubColumn()) + if err != nil { + return err + } + + // Init encoder for SpanID field. + if e.fieldCount <= 9 { + return nil // SpanID and all subsequent fields are skipped. + } + err = e.spanIDEncoder.Init(e.limiter, columns.AddSubColumn()) + if err != nil { + return err + } + + // Init encoder for EventName field. + if e.fieldCount <= 10 { + return nil // EventName and all subsequent fields are skipped. + } + err = e.eventNameEncoder.Init(&state.LogEventName, e.limiter, columns.AddSubColumn()) + if err != nil { + return err + } + + return nil +} + +func (e *LogRecordEncoder) Reset() { + // Since we are resetting the state of encoder make sure the next Encode() + // call forcedly writes all fields and does not attempt to skip. + e.forceModifiedFields = e.keepFieldMask + + if e.fieldCount <= 0 { + return // TimeUnixNano and all subsequent fields are skipped. + } + e.timeUnixNanoEncoder.Reset() + if e.fieldCount <= 1 { + return // ObservedTimeUnixNano and all subsequent fields are skipped. + } + e.observedTimeUnixNanoEncoder.Reset() + if e.fieldCount <= 2 { + return // SeverityNumber and all subsequent fields are skipped. + } + e.severityNumberEncoder.Reset() + if e.fieldCount <= 3 { + return // SeverityText and all subsequent fields are skipped. + } + e.severityTextEncoder.Reset() + if e.fieldCount <= 4 { + return // Body and all subsequent fields are skipped. + } + if !e.isBodyRecursive { + e.bodyEncoder.Reset() + } + if e.fieldCount <= 5 { + return // Attributes and all subsequent fields are skipped. + } + if !e.isAttributesRecursive { + e.attributesEncoder.Reset() + } + if e.fieldCount <= 6 { + return // DroppedAttributesCount and all subsequent fields are skipped. + } + e.droppedAttributesCountEncoder.Reset() + if e.fieldCount <= 7 { + return // Flags and all subsequent fields are skipped. + } + e.flagsEncoder.Reset() + if e.fieldCount <= 8 { + return // TraceID and all subsequent fields are skipped. + } + e.traceIDEncoder.Reset() + if e.fieldCount <= 9 { + return // SpanID and all subsequent fields are skipped. + } + e.spanIDEncoder.Reset() + if e.fieldCount <= 10 { + return // EventName and all subsequent fields are skipped. + } + e.eventNameEncoder.Reset() +} + +// Encode encodes val into buf +func (e *LogRecordEncoder) Encode(val *LogRecord) { + var bitCount uint + + // Mask that describes what fields are encoded. Start with all modified fields. + fieldMask := val.modifiedFields.mask + + // If forceModifiedFields we need to set to 1 all bits so that we + // force writing of all fields. + fieldMask |= e.forceModifiedFields + e.forceModifiedFields = 0 + + // Only write fields that we want to write. See Init() for keepFieldMask. + fieldMask &= e.keepFieldMask + + // Write bits to indicate which fields follow. + e.buf.WriteBits(fieldMask, e.fieldCount) + bitCount += e.fieldCount + + // Encode modified, present fields. + + if fieldMask&fieldModifiedLogRecordTimeUnixNano != 0 { + // Encode TimeUnixNano + e.timeUnixNanoEncoder.Encode(val.timeUnixNano) + } + + if fieldMask&fieldModifiedLogRecordObservedTimeUnixNano != 0 { + // Encode ObservedTimeUnixNano + e.observedTimeUnixNanoEncoder.Encode(val.observedTimeUnixNano) + } + + if fieldMask&fieldModifiedLogRecordSeverityNumber != 0 { + // Encode SeverityNumber + e.severityNumberEncoder.Encode(val.severityNumber) + } + + if fieldMask&fieldModifiedLogRecordSeverityText != 0 { + // Encode SeverityText + e.severityTextEncoder.Encode(val.severityText) + } + + if fieldMask&fieldModifiedLogRecordBody != 0 { + // Encode Body + e.bodyEncoder.Encode(val.body) + } + + if fieldMask&fieldModifiedLogRecordAttributes != 0 { + // Encode Attributes + e.attributesEncoder.Encode(&val.attributes) + } + + if fieldMask&fieldModifiedLogRecordDroppedAttributesCount != 0 { + // Encode DroppedAttributesCount + e.droppedAttributesCountEncoder.Encode(val.droppedAttributesCount) + } + + if fieldMask&fieldModifiedLogRecordFlags != 0 { + // Encode Flags + e.flagsEncoder.Encode(val.flags) + } + + if fieldMask&fieldModifiedLogRecordTraceID != 0 { + // Encode TraceID + e.traceIDEncoder.Encode(val.traceID) + } + + if fieldMask&fieldModifiedLogRecordSpanID != 0 { + // Encode SpanID + e.spanIDEncoder.Encode(val.spanID) + } + + if fieldMask&fieldModifiedLogRecordEventName != 0 { + // Encode EventName + e.eventNameEncoder.Encode(val.eventName) + } + + // Account written bits in the limiter. + e.limiter.AddFrameBits(bitCount) + + // Mark all fields non-modified so that next Encode() correctly + // encodes only fields that change after this. + val.modifiedFields.mask = 0 +} + +// CollectColumns collects all buffers from all encoders into buf. +func (e *LogRecordEncoder) CollectColumns(columnSet *pkg.WriteColumnSet) { + columnSet.SetBits(&e.buf) + colIdx := 0 + // Collect TimeUnixNano field. + if e.fieldCount <= 0 { + return // TimeUnixNano and subsequent fields are skipped. + } + e.timeUnixNanoEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + // Collect ObservedTimeUnixNano field. + if e.fieldCount <= 1 { + return // ObservedTimeUnixNano and subsequent fields are skipped. + } + e.observedTimeUnixNanoEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + // Collect SeverityNumber field. + if e.fieldCount <= 2 { + return // SeverityNumber and subsequent fields are skipped. + } + e.severityNumberEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + // Collect SeverityText field. + if e.fieldCount <= 3 { + return // SeverityText and subsequent fields are skipped. + } + e.severityTextEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + // Collect Body field. + if e.fieldCount <= 4 { + return // Body and subsequent fields are skipped. + } + if !e.isBodyRecursive { + e.bodyEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + } + // Collect Attributes field. + if e.fieldCount <= 5 { + return // Attributes and subsequent fields are skipped. + } + if !e.isAttributesRecursive { + e.attributesEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + } + // Collect DroppedAttributesCount field. + if e.fieldCount <= 6 { + return // DroppedAttributesCount and subsequent fields are skipped. + } + e.droppedAttributesCountEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + // Collect Flags field. + if e.fieldCount <= 7 { + return // Flags and subsequent fields are skipped. + } + e.flagsEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + // Collect TraceID field. + if e.fieldCount <= 8 { + return // TraceID and subsequent fields are skipped. + } + e.traceIDEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + // Collect SpanID field. + if e.fieldCount <= 9 { + return // SpanID and subsequent fields are skipped. + } + e.spanIDEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + // Collect EventName field. + if e.fieldCount <= 10 { + return // EventName and subsequent fields are skipped. + } + e.eventNameEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ +} + +// LogRecordDecoder implements decoding of LogRecord +type LogRecordDecoder struct { + buf pkg.BitsReader + column *pkg.ReadableColumn + fieldCount uint + + timeUnixNanoDecoder codecs.Uint64Decoder + + observedTimeUnixNanoDecoder codecs.Uint64Decoder + + severityNumberDecoder codecs.Uint64Decoder + + severityTextDecoder codecs.StringDictDecoder + + bodyDecoder *AnyValueDecoder + isBodyRecursive bool + attributesDecoder *AttributesDecoder + isAttributesRecursive bool + droppedAttributesCountDecoder codecs.Uint64Decoder + + flagsDecoder codecs.Uint64Decoder + + traceIDDecoder codecs.BytesDecoder + + spanIDDecoder codecs.BytesDecoder + + eventNameDecoder codecs.StringDictDecoder + + allocators *Allocators +} + +// Init is called once in the lifetime of the stream. +func (d *LogRecordDecoder) Init(state *ReaderState, columns *pkg.ReadColumnSet) error { + // Remember this decoder in the state so that we can detect recursion. + if state.LogRecordDecoder != nil { + panic("cannot initialize LogRecordDecoder: already initialized") + } + state.LogRecordDecoder = d + defer func() { state.LogRecordDecoder = nil }() + + d.allocators = &state.Allocators + + // Number of fields in the input data schema. + var err error + d.fieldCount, err = state.StructFieldCounts.LogRecordFieldCount() + if err != nil { + return fmt.Errorf("cannot find struct %s in override schema: %w", "LogRecord", err) + } + if d.fieldCount > 11 { + return pkg.ErrTooManyFieldsToDecode + } + + d.column = columns.Column() + + if d.fieldCount <= 0 { + return nil // TimeUnixNano and subsequent fields are skipped. + } + err = d.timeUnixNanoDecoder.Init(columns.AddSubColumn()) + if err != nil { + return err + } + if d.fieldCount <= 1 { + return nil // ObservedTimeUnixNano and subsequent fields are skipped. + } + err = d.observedTimeUnixNanoDecoder.Init(columns.AddSubColumn()) + if err != nil { + return err + } + if d.fieldCount <= 2 { + return nil // SeverityNumber and subsequent fields are skipped. + } + err = d.severityNumberDecoder.Init(columns.AddSubColumn()) + if err != nil { + return err + } + if d.fieldCount <= 3 { + return nil // SeverityText and subsequent fields are skipped. + } + err = d.severityTextDecoder.Init(&state.LogSeverityText, columns.AddSubColumn()) + if err != nil { + return err + } + if d.fieldCount <= 4 { + return nil // Body and subsequent fields are skipped. + } + if state.AnyValueDecoder != nil { + // Recursion detected, use the existing decoder. + d.bodyDecoder = state.AnyValueDecoder + d.isBodyRecursive = true // Mark that we are using a recursive decoder. + } else { + d.bodyDecoder = new(AnyValueDecoder) + err = d.bodyDecoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + if d.fieldCount <= 5 { + return nil // Attributes and subsequent fields are skipped. + } + if state.AttributesDecoder != nil { + // Recursion detected, use the existing decoder. + d.attributesDecoder = state.AttributesDecoder + d.isAttributesRecursive = true // Mark that we are using a recursive decoder. + } else { + d.attributesDecoder = new(AttributesDecoder) + err = d.attributesDecoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + if d.fieldCount <= 6 { + return nil // DroppedAttributesCount and subsequent fields are skipped. + } + err = d.droppedAttributesCountDecoder.Init(columns.AddSubColumn()) + if err != nil { + return err + } + if d.fieldCount <= 7 { + return nil // Flags and subsequent fields are skipped. + } + err = d.flagsDecoder.Init(columns.AddSubColumn()) + if err != nil { + return err + } + if d.fieldCount <= 8 { + return nil // TraceID and subsequent fields are skipped. + } + err = d.traceIDDecoder.Init(columns.AddSubColumn()) + if err != nil { + return err + } + if d.fieldCount <= 9 { + return nil // SpanID and subsequent fields are skipped. + } + err = d.spanIDDecoder.Init(columns.AddSubColumn()) + if err != nil { + return err + } + if d.fieldCount <= 10 { + return nil // EventName and subsequent fields are skipped. + } + err = d.eventNameDecoder.Init(&state.LogEventName, columns.AddSubColumn()) + if err != nil { + return err + } + + return nil +} + +// Continue is called at the start of the frame to continue decoding column data. +// This should set the decoder's source buffer, so the new decoding continues from +// the supplied column data. This should NOT reset the internal state of the decoder, +// since columns can cross frame boundaries and the new column data is considered +// continuation of that same column in the previous frame. +func (d *LogRecordDecoder) Continue() { + d.buf.Reset(d.column.Data()) + + if d.fieldCount <= 0 { + return // TimeUnixNano and subsequent fields are skipped. + } + d.timeUnixNanoDecoder.Continue() + if d.fieldCount <= 1 { + return // ObservedTimeUnixNano and subsequent fields are skipped. + } + d.observedTimeUnixNanoDecoder.Continue() + if d.fieldCount <= 2 { + return // SeverityNumber and subsequent fields are skipped. + } + d.severityNumberDecoder.Continue() + if d.fieldCount <= 3 { + return // SeverityText and subsequent fields are skipped. + } + d.severityTextDecoder.Continue() + if d.fieldCount <= 4 { + return // Body and subsequent fields are skipped. + } + + if !d.isBodyRecursive { + d.bodyDecoder.Continue() + } + + if d.fieldCount <= 5 { + return // Attributes and subsequent fields are skipped. + } + + if !d.isAttributesRecursive { + d.attributesDecoder.Continue() + } + + if d.fieldCount <= 6 { + return // DroppedAttributesCount and subsequent fields are skipped. + } + d.droppedAttributesCountDecoder.Continue() + if d.fieldCount <= 7 { + return // Flags and subsequent fields are skipped. + } + d.flagsDecoder.Continue() + if d.fieldCount <= 8 { + return // TraceID and subsequent fields are skipped. + } + d.traceIDDecoder.Continue() + if d.fieldCount <= 9 { + return // SpanID and subsequent fields are skipped. + } + d.spanIDDecoder.Continue() + if d.fieldCount <= 10 { + return // EventName and subsequent fields are skipped. + } + d.eventNameDecoder.Continue() +} + +func (d *LogRecordDecoder) Reset() { + + if d.fieldCount <= 0 { + return // TimeUnixNano and all subsequent fields are skipped. + } + d.timeUnixNanoDecoder.Reset() + if d.fieldCount <= 1 { + return // ObservedTimeUnixNano and all subsequent fields are skipped. + } + d.observedTimeUnixNanoDecoder.Reset() + if d.fieldCount <= 2 { + return // SeverityNumber and all subsequent fields are skipped. + } + d.severityNumberDecoder.Reset() + if d.fieldCount <= 3 { + return // SeverityText and all subsequent fields are skipped. + } + d.severityTextDecoder.Reset() + if d.fieldCount <= 4 { + return // Body and all subsequent fields are skipped. + } + + if !d.isBodyRecursive { + d.bodyDecoder.Reset() + } + + if d.fieldCount <= 5 { + return // Attributes and all subsequent fields are skipped. + } + + if !d.isAttributesRecursive { + d.attributesDecoder.Reset() + } + + if d.fieldCount <= 6 { + return // DroppedAttributesCount and all subsequent fields are skipped. + } + d.droppedAttributesCountDecoder.Reset() + if d.fieldCount <= 7 { + return // Flags and all subsequent fields are skipped. + } + d.flagsDecoder.Reset() + if d.fieldCount <= 8 { + return // TraceID and all subsequent fields are skipped. + } + d.traceIDDecoder.Reset() + if d.fieldCount <= 9 { + return // SpanID and all subsequent fields are skipped. + } + d.spanIDDecoder.Reset() + if d.fieldCount <= 10 { + return // EventName and all subsequent fields are skipped. + } + d.eventNameDecoder.Reset() +} + +func (d *LogRecordDecoder) Decode(dstPtr *LogRecord) error { + val := dstPtr + + // Read bits that indicate which fields follow. + val.modifiedFields.mask = d.buf.PeekBits(d.fieldCount) + d.buf.Consume(d.fieldCount) + + if val.modifiedFields.mask&fieldModifiedLogRecordTimeUnixNano != 0 { // TimeUnixNano is changed. + + if err := d.timeUnixNanoDecoder.Decode(&val.timeUnixNano); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogRecordObservedTimeUnixNano != 0 { // ObservedTimeUnixNano is changed. + + if err := d.observedTimeUnixNanoDecoder.Decode(&val.observedTimeUnixNano); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogRecordSeverityNumber != 0 { // SeverityNumber is changed. + + if err := d.severityNumberDecoder.Decode(&val.severityNumber); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogRecordSeverityText != 0 { // SeverityText is changed. + + if err := d.severityTextDecoder.Decode(&val.severityText); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogRecordBody != 0 { // Body is changed. + + if val.body == nil { + if err := d.allocators.allocSizeChecker.PrepAllocSize(uint(unsafe.Sizeof(*val.body))); err != nil { + return err + } + val.body = d.allocators.AnyValue.Alloc() + val.body.init(&val.modifiedFields, fieldModifiedLogRecordBody) + } + + if err := d.bodyDecoder.Decode(val.body); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogRecordAttributes != 0 { // Attributes is changed. + + if err := d.attributesDecoder.Decode(&val.attributes); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogRecordDroppedAttributesCount != 0 { // DroppedAttributesCount is changed. + + if err := d.droppedAttributesCountDecoder.Decode(&val.droppedAttributesCount); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogRecordFlags != 0 { // Flags is changed. + + if err := d.flagsDecoder.Decode(&val.flags); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogRecordTraceID != 0 { // TraceID is changed. + + if err := d.traceIDDecoder.Decode(&val.traceID); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogRecordSpanID != 0 { // SpanID is changed. + + if err := d.spanIDDecoder.Decode(&val.spanID); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogRecordEventName != 0 { // EventName is changed. + + if err := d.eventNameDecoder.Decode(&val.eventName); err != nil { + return err + } + } + + return d.buf.Error() +} + +// LogRecordAllocator implements a custom allocator for LogRecord. +// It maintains a pool of pre-allocated LogRecord and grows the pool +// dynamically as needed, up to a maximum size of 64 elements. +type LogRecordAllocator struct { + pool []LogRecord + ofs int +} + +// Alloc returns the next available LogRecord from the pool. +// If the pool is exhausted, it grows the pool by doubling its size +// up to a maximum of 64 elements. +func (a *LogRecordAllocator) Alloc() *LogRecord { + if a.ofs < len(a.pool) { + // Get the next available LogRecord from the pool + a.ofs++ + return &a.pool[a.ofs-1] + } + // We've exhausted the current pool, prealloc a new pool. + return a.prealloc() +} + +//go:noinline +func (a *LogRecordAllocator) prealloc() *LogRecord { + // prealloc expands the pool by doubling its size, up to a maximum of 64 elements. + // If the pool is empty, it starts with 1 element. + newLen := min(max(len(a.pool)*2, 1), 64) + a.pool = make([]LogRecord, newLen) + a.ofs = 1 + return &a.pool[0] +} diff --git a/go/otel/otelstef/logs.go b/go/otel/otelstef/logs.go new file mode 100644 index 00000000..1199900e --- /dev/null +++ b/go/otel/otelstef/logs.go @@ -0,0 +1,1027 @@ +// Code generated by stefc. DO NOT EDIT. +package otelstef + +import ( + "bytes" + "fmt" + "math/rand/v2" + "strings" + "unsafe" + + "github.com/splunk/stef/go/pkg" + "github.com/splunk/stef/go/pkg/codecs" + "github.com/splunk/stef/go/pkg/schema" +) + +var _ = strings.Compare +var _ = codecs.StringEncoder{} +var _ = schema.WireSchema{} +var _ = bytes.NewBuffer + +type Logs struct { + envelope Envelope + resource *Resource + scope *Scope + log LogRecord + + // modifiedFields keeps track of which fields are modified. + modifiedFields modifiedFields +} + +const LogsStructName = "Logs" + +// Bitmasks for "modified" flags for each field. +const ( + fieldModifiedLogsEnvelope = uint64(1 << iota) + fieldModifiedLogsResource + fieldModifiedLogsScope + fieldModifiedLogsLog +) + +// Init must be called once, before the Logs is used. +func (s *Logs) Init() { + s.init(nil, 0) +} + +func NewLogs() *Logs { + var s Logs + s.init(nil, 0) + return &s +} + +func (s *Logs) init(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + + s.envelope.init(&s.modifiedFields, fieldModifiedLogsEnvelope) + s.resource = &Resource{} + s.resource.init(&s.modifiedFields, fieldModifiedLogsResource) + s.scope = &Scope{} + s.scope.init(&s.modifiedFields, fieldModifiedLogsScope) + s.log.init(&s.modifiedFields, fieldModifiedLogsLog) +} + +func (s *Logs) initAlloc(parentModifiedFields *modifiedFields, parentModifiedBit uint64, allocators *Allocators) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + + s.envelope.initAlloc(&s.modifiedFields, fieldModifiedLogsEnvelope, allocators) + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Resource{}))) + s.resource = allocators.Resource.Alloc() + s.resource.initAlloc(&s.modifiedFields, fieldModifiedLogsResource, allocators) + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Scope{}))) + s.scope = allocators.Scope.Alloc() + s.scope.initAlloc(&s.modifiedFields, fieldModifiedLogsScope, allocators) + s.log.initAlloc(&s.modifiedFields, fieldModifiedLogsLog, allocators) +} + +// Reset the struct to its initial state. +func (s *Logs) Reset() { + s.reset() + s.setUnmodifiedRecursively() +} + +// reset the struct to its initial state, as if init() was just called. +// Will not reset internal fields such as parentModifiedFields. +func (s *Logs) reset() { + if s.modifiedFields.isFrozen() { + panic("cannot modify frozen Logs") + } + s.envelope.reset() + s.resource = &emptyResource + s.scope = &emptyScope + s.log.reset() +} + +// fixParent sets the parentModifiedFields pointer to the supplied value. +// This is used when the parent is moved in memory for example because the parent +// an array element and the array was expanded. +func (s *Logs) fixParent(parentModifiedFields *modifiedFields) { + s.modifiedFields.parent = parentModifiedFields + s.envelope.fixParent(&s.modifiedFields) + s.resource.fixParent(&s.modifiedFields) + s.scope.fixParent(&s.modifiedFields) + s.log.fixParent(&s.modifiedFields) +} + +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Logs) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.envelope.attachParent(&s.modifiedFields, fieldModifiedLogsEnvelope) + if !s.resource.canBeShared() { + s.resource.attachParent(&s.modifiedFields, fieldModifiedLogsResource) + } + if !s.scope.canBeShared() { + s.scope.attachParent(&s.modifiedFields, fieldModifiedLogsScope) + } + s.log.attachParent(&s.modifiedFields, fieldModifiedLogsLog) +} + +func (s *Logs) freeze() { + if s.isFrozen() { + return + } + s.modifiedFields.freeze() + s.envelope.freeze() + s.resource.freeze() + s.scope.freeze() + s.log.freeze() +} + +func (s *Logs) isFrozen() bool { + return s.modifiedFields.isFrozen() +} + +func (s *Logs) Envelope() *Envelope { + return &s.envelope +} + +func (s *Logs) markEnvelopeModified() { + s.modifiedFields.markModified(fieldModifiedLogsEnvelope) +} + +// IsEnvelopeModified returns true the value of Envelope field was modified since +// Logs was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *Logs) IsEnvelopeModified() bool { + return s.modifiedFields.mask&fieldModifiedLogsEnvelope != 0 +} + +// Resource returns a readonly value. Use SetResource() to modify it. +func (s *Logs) Resource() *Resource { + return s.resource +} + +// SetResource sets the value of Resource field. +func (s *Logs) SetResource(v *Resource) { + if v.canBeShared() { + // v can be shared by pointer. Compute its difference from current resource + if v.computeDiff(s.resource) { + // It is different. Update to it. + s.resource = v + s.modifiedFields.markModified(fieldModifiedLogsResource) + } + } else { + if s.resource.canBeShared() { + s.resource = s.resource.clone(&Allocators{}) + } + s.resource.CopyFrom(v) + s.modifiedFields.markModified(fieldModifiedLogsResource) + } +} + +func (s *Logs) markResourceModified() { + s.modifiedFields.markModified(fieldModifiedLogsResource) +} + +// IsResourceModified returns true the value of Resource field was modified since +// Logs was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *Logs) IsResourceModified() bool { + return s.modifiedFields.mask&fieldModifiedLogsResource != 0 +} + +// Scope returns a readonly value. Use SetScope() to modify it. +func (s *Logs) Scope() *Scope { + return s.scope +} + +// SetScope sets the value of Scope field. +func (s *Logs) SetScope(v *Scope) { + if v.canBeShared() { + // v can be shared by pointer. Compute its difference from current scope + if v.computeDiff(s.scope) { + // It is different. Update to it. + s.scope = v + s.modifiedFields.markModified(fieldModifiedLogsScope) + } + } else { + if s.scope.canBeShared() { + s.scope = s.scope.clone(&Allocators{}) + } + s.scope.CopyFrom(v) + s.modifiedFields.markModified(fieldModifiedLogsScope) + } +} + +func (s *Logs) markScopeModified() { + s.modifiedFields.markModified(fieldModifiedLogsScope) +} + +// IsScopeModified returns true the value of Scope field was modified since +// Logs was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *Logs) IsScopeModified() bool { + return s.modifiedFields.mask&fieldModifiedLogsScope != 0 +} + +func (s *Logs) Log() *LogRecord { + return &s.log +} + +func (s *Logs) markLogModified() { + s.modifiedFields.markModified(fieldModifiedLogsLog) +} + +// IsLogModified returns true the value of Log field was modified since +// Logs was created, encoded or decoded. If the field is modified +// it will be encoded by the next Write() operation. If the field is decoded by the +// next Read() operation the modified flag will be set. +func (s *Logs) IsLogModified() bool { + return s.modifiedFields.mask&fieldModifiedLogsLog != 0 +} + +func (s *Logs) setModifiedRecursively() { + s.envelope.setModifiedRecursively() + s.resource.setModifiedRecursively() + s.scope.setModifiedRecursively() + s.log.setModifiedRecursively() + s.modifiedFields.mask = + fieldModifiedLogsEnvelope | + fieldModifiedLogsResource | + fieldModifiedLogsScope | + fieldModifiedLogsLog | 0 +} + +func (s *Logs) setUnmodifiedRecursively() { + if s.IsEnvelopeModified() { + s.envelope.setUnmodifiedRecursively() + } + if s.IsResourceModified() { + s.resource.setUnmodifiedRecursively() + } + if s.IsScopeModified() { + s.scope.setUnmodifiedRecursively() + } + if s.IsLogModified() { + s.log.setUnmodifiedRecursively() + } + s.modifiedFields.mask = 0 +} + +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Logs) clearModifiedRecursively() { + s.envelope.clearModifiedRecursively() + if !s.resource.canBeShared() { + s.resource.clearModifiedRecursively() + } + if !s.scope.canBeShared() { + s.scope.clearModifiedRecursively() + } + s.log.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + +// computeDiff compares s and val and returns true if they differ. +// All fields that are different in s will be marked as modified. +func (s *Logs) computeDiff(val *Logs) (ret bool) { + // Compare Envelope field. + if s.envelope.computeDiff(&val.envelope) { + s.modifiedFields.setModified(fieldModifiedLogsEnvelope) + ret = true + } + // Compare Resource field. + if s.resource.computeDiff(val.resource) { + s.modifiedFields.setModified(fieldModifiedLogsResource) + ret = true + } + // Compare Scope field. + if s.scope.computeDiff(val.scope) { + s.modifiedFields.setModified(fieldModifiedLogsScope) + ret = true + } + // Compare Log field. + if s.log.computeDiff(&val.log) { + s.modifiedFields.setModified(fieldModifiedLogsLog) + ret = true + } + return ret +} + +// canBeShared returns true if s is safe to share by pointer without cloning (for example if s is frozen). +func (s *Logs) canBeShared() bool { + return false +} + +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Logs) CloneTo(dst *Logs, allocators *Allocators) { + *dst = Logs{} + copyToNewEnvelope(&dst.envelope, &s.envelope, allocators) + dst.resource = s.resource.cloneShared(allocators) + dst.scope = s.scope.cloneShared(allocators) + copyToNewLogRecord(&dst.log, &s.log, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() +} + +// ByteSize returns approximate memory usage in bytes. Used to calculate +// memory used by dictionaries. +func (s *Logs) byteSize() uint { + return uint(unsafe.Sizeof(*s)) + + s.envelope.byteSize() + s.resource.byteSize() + s.scope.byteSize() + s.log.byteSize() + 0 +} + +// Copy from src to dst, overwriting existing data in dst. +func copyLogs(dst *Logs, src *Logs) { + copyEnvelope(&dst.envelope, &src.envelope) + + if src.resource.canBeShared() { + if src.resource.computeDiff(dst.resource) { + dst.resource = src.resource + dst.markResourceModified() + } + } else { + if dst.resource == nil || dst.resource.canBeShared() { // Not allowed to modify shared data. + dst.resource = new(Resource) + dst.resource.init(&dst.modifiedFields, fieldModifiedLogsResource) + } + copyResource(dst.resource, src.resource) + } + + if src.scope.canBeShared() { + if src.scope.computeDiff(dst.scope) { + dst.scope = src.scope + dst.markScopeModified() + } + } else { + if dst.scope == nil || dst.scope.canBeShared() { // Not allowed to modify shared data. + dst.scope = new(Scope) + dst.scope.init(&dst.modifiedFields, fieldModifiedLogsScope) + } + copyScope(dst.scope, src.scope) + } + copyLogRecord(&dst.log, &src.log) +} + +// Copy from src to dst. dst is assumed to be just inited. +func copyToNewLogs(dst *Logs, src *Logs, allocators *Allocators) { + copyToNewEnvelope(&dst.envelope, &src.envelope, allocators) + + if src.resource.canBeShared() { + dst.resource = src.resource + } else { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Resource{}))) + dst.resource = allocators.Resource.Alloc() + dst.resource.init(&dst.modifiedFields, fieldModifiedLogsResource) + copyToNewResource(dst.resource, src.resource, allocators) + } + + if src.scope.canBeShared() { + dst.scope = src.scope + } else { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Scope{}))) + dst.scope = allocators.Scope.Alloc() + dst.scope.init(&dst.modifiedFields, fieldModifiedLogsScope) + copyToNewScope(dst.scope, src.scope, allocators) + } + + copyToNewLogRecord(&dst.log, &src.log, allocators) +} + +// CopyFrom() performs a deep copy from src. +func (s *Logs) CopyFrom(src *Logs) { + copyLogs(s, src) +} + +// mutateRandom mutates fields in a random, deterministic manner using +// random parameter as a deterministic generator. Only fields that exist +// in the schem are mutated, allowing to generate data for specified schema. +// The state of "modified" flags is undefined after this operation. +func (s *Logs) mutateRandom(random *rand.Rand, schem *schema.Schema, limiter *mutateRandomLimiter) { + // Get the field count for this struct from the schema. If the schema specifies + // fewer field count than the one we have in this code then we will not mutate + // fields that are not in the schema. + fieldCount, err := schem.FieldCount("Logs") + if err != nil { + panic(fmt.Sprintf("cannot get field count for %s: %v", "Logs", err)) + } + + const randRange = max(4, 2) // At least 2 to ensure we don't recurse infinitely if there is only 1 field. + + if fieldCount <= 0 { + return // Envelope and all subsequent fields are skipped. + } + // Maybe mutate Envelope + if random.IntN(randRange) == 0 { + s.envelope.mutateRandom(random, schem, limiter) + } + if fieldCount <= 1 { + return // Resource and all subsequent fields are skipped. + } + // Maybe mutate Resource + if random.IntN(randRange) == 0 { + if random.IntN(10) == 0 { + // Freeze and replace with a clone to test frozen object dictionary handling. + s.resource.Freeze() + if random.IntN(10) == 0 { + // Reset to brand new object once in a while to test the code path + // where a dict-based is not mutated, but created from scratch. + s.resource = new(Resource) + s.resource.init(&s.modifiedFields, fieldModifiedLogsResource) + } else { + s.resource = s.resource.clone(&Allocators{}) + } + } + if s.resource.canBeShared() { + // resource may be shared by pointer. Clone it to have exclusive ownership. + s.resource = s.resource.clone(&Allocators{}) + } + + s.resource.mutateRandom(random, schem, limiter) + } + if fieldCount <= 2 { + return // Scope and all subsequent fields are skipped. + } + // Maybe mutate Scope + if random.IntN(randRange) == 0 { + if random.IntN(10) == 0 { + // Freeze and replace with a clone to test frozen object dictionary handling. + s.scope.Freeze() + if random.IntN(10) == 0 { + // Reset to brand new object once in a while to test the code path + // where a dict-based is not mutated, but created from scratch. + s.scope = new(Scope) + s.scope.init(&s.modifiedFields, fieldModifiedLogsScope) + } else { + s.scope = s.scope.clone(&Allocators{}) + } + } + if s.scope.canBeShared() { + // scope may be shared by pointer. Clone it to have exclusive ownership. + s.scope = s.scope.clone(&Allocators{}) + } + + s.scope.mutateRandom(random, schem, limiter) + } + if fieldCount <= 3 { + return // Log and all subsequent fields are skipped. + } + // Maybe mutate Log + if random.IntN(randRange) == 0 { + s.log.mutateRandom(random, schem, limiter) + } +} + +// IsEqual performs deep comparison and returns true if struct is equal to right. +func (s *Logs) IsEqual(right *Logs) bool { + // Compare Envelope field. + if !s.envelope.IsEqual(&right.envelope) { + return false + } + // Compare Resource field. + if !s.resource.IsEqual(right.resource) { + return false + } + // Compare Scope field. + if !s.scope.IsEqual(right.scope) { + return false + } + // Compare Log field. + if !s.log.IsEqual(&right.log) { + return false + } + + return true +} + +// CmpLogs performs deep comparison and returns an integer that +// will be 0 if left == right, negative if left < right, positive if left > right. +func CmpLogs(left, right *Logs) int { + // Compare Envelope field. + if c := CmpEnvelope(&left.envelope, &right.envelope); c != 0 { + return c + } + // Compare Resource field. + if c := CmpResource(left.resource, right.resource); c != 0 { + return c + } + // Compare Scope field. + if c := CmpScope(left.scope, right.scope); c != 0 { + return c + } + // Compare Log field. + if c := CmpLogRecord(&left.log, &right.log); c != 0 { + return c + } + return 0 +} + +// LogsEncoder implements encoding of Logs +type LogsEncoder struct { + buf pkg.BitsWriter + limiter *pkg.SizeLimiter + + // forceModifiedFields is set to a mask to force the next encoding operation + // write the fields, whether they are modified or no. This is used after frame + // restarts so that the data can be decoded from the frame start. + forceModifiedFields uint64 + + envelopeEncoder *EnvelopeEncoder + isEnvelopeRecursive bool // Indicates Envelope field's type is recursive. + resourceEncoder *ResourceEncoder + isResourceRecursive bool // Indicates Resource field's type is recursive. + scopeEncoder *ScopeEncoder + isScopeRecursive bool // Indicates Scope field's type is recursive. + logEncoder *LogRecordEncoder + isLogRecursive bool // Indicates Log field's type is recursive. + + allocators *Allocators + + keepFieldMask uint64 + fieldCount uint +} + +func (e *LogsEncoder) Init(state *WriterState, columns *pkg.WriteColumnSet) error { + // Remember this encoder in the state so that we can detect recursion. + if state.LogsEncoder != nil { + panic("cannot initialize LogsEncoder: already initialized") + } + state.LogsEncoder = e + defer func() { state.LogsEncoder = nil }() + + e.limiter = &state.limiter + e.allocators = &state.Allocators + + // Number of fields in the output data schema. + var err error + e.fieldCount, err = state.StructFieldCounts.LogsFieldCount() + if err != nil { + return fmt.Errorf("cannot find struct %s in override schema: %w", "Logs", err) + } + // Set that many 1 bits in the keepFieldMask. All fields with higher number + // will be skipped when encoding. + e.keepFieldMask = ^(^uint64(0) << e.fieldCount) + + // Init encoder for Envelope field. + if e.fieldCount <= 0 { + return nil // Envelope and all subsequent fields are skipped. + } + if state.EnvelopeEncoder != nil { + // Recursion detected, use the existing encoder. + e.envelopeEncoder = state.EnvelopeEncoder + e.isEnvelopeRecursive = true + } else { + e.envelopeEncoder = new(EnvelopeEncoder) + err = e.envelopeEncoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + + // Init encoder for Resource field. + if e.fieldCount <= 1 { + return nil // Resource and all subsequent fields are skipped. + } + if state.ResourceEncoder != nil { + // Recursion detected, use the existing encoder. + e.resourceEncoder = state.ResourceEncoder + e.isResourceRecursive = true + } else { + e.resourceEncoder = new(ResourceEncoder) + err = e.resourceEncoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + + // Init encoder for Scope field. + if e.fieldCount <= 2 { + return nil // Scope and all subsequent fields are skipped. + } + if state.ScopeEncoder != nil { + // Recursion detected, use the existing encoder. + e.scopeEncoder = state.ScopeEncoder + e.isScopeRecursive = true + } else { + e.scopeEncoder = new(ScopeEncoder) + err = e.scopeEncoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + + // Init encoder for Log field. + if e.fieldCount <= 3 { + return nil // Log and all subsequent fields are skipped. + } + if state.LogRecordEncoder != nil { + // Recursion detected, use the existing encoder. + e.logEncoder = state.LogRecordEncoder + e.isLogRecursive = true + } else { + e.logEncoder = new(LogRecordEncoder) + err = e.logEncoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + + return nil +} + +func (e *LogsEncoder) Reset() { + // Since we are resetting the state of encoder make sure the next Encode() + // call forcedly writes all fields and does not attempt to skip. + e.forceModifiedFields = e.keepFieldMask + + if e.fieldCount <= 0 { + return // Envelope and all subsequent fields are skipped. + } + if !e.isEnvelopeRecursive { + e.envelopeEncoder.Reset() + } + if e.fieldCount <= 1 { + return // Resource and all subsequent fields are skipped. + } + if !e.isResourceRecursive { + e.resourceEncoder.Reset() + } + if e.fieldCount <= 2 { + return // Scope and all subsequent fields are skipped. + } + if !e.isScopeRecursive { + e.scopeEncoder.Reset() + } + if e.fieldCount <= 3 { + return // Log and all subsequent fields are skipped. + } + if !e.isLogRecursive { + e.logEncoder.Reset() + } +} + +// Encode encodes val into buf +func (e *LogsEncoder) Encode(val *Logs) { + var bitCount uint + + // Mask that describes what fields are encoded. Start with all modified fields. + fieldMask := val.modifiedFields.mask + + // If forceModifiedFields we need to set to 1 all bits so that we + // force writing of all fields. + fieldMask |= e.forceModifiedFields + e.forceModifiedFields = 0 + + // Only write fields that we want to write. See Init() for keepFieldMask. + fieldMask &= e.keepFieldMask + + // Write bits to indicate which fields follow. + e.buf.WriteBits(fieldMask, e.fieldCount) + bitCount += e.fieldCount + + // Encode modified, present fields. + + if fieldMask&fieldModifiedLogsEnvelope != 0 { + // Encode Envelope + e.envelopeEncoder.Encode(&val.envelope) + } + + if fieldMask&fieldModifiedLogsResource != 0 { + // Encode Resource + e.resourceEncoder.Encode(val.resource) + } + + if fieldMask&fieldModifiedLogsScope != 0 { + // Encode Scope + e.scopeEncoder.Encode(val.scope) + } + + if fieldMask&fieldModifiedLogsLog != 0 { + // Encode Log + e.logEncoder.Encode(&val.log) + } + + // Account written bits in the limiter. + e.limiter.AddFrameBits(bitCount) + + // Mark all fields non-modified so that next Encode() correctly + // encodes only fields that change after this. + val.modifiedFields.mask = 0 +} + +// CollectColumns collects all buffers from all encoders into buf. +func (e *LogsEncoder) CollectColumns(columnSet *pkg.WriteColumnSet) { + columnSet.SetBits(&e.buf) + colIdx := 0 + // Collect Envelope field. + if e.fieldCount <= 0 { + return // Envelope and subsequent fields are skipped. + } + if !e.isEnvelopeRecursive { + e.envelopeEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + } + // Collect Resource field. + if e.fieldCount <= 1 { + return // Resource and subsequent fields are skipped. + } + if !e.isResourceRecursive { + e.resourceEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + } + // Collect Scope field. + if e.fieldCount <= 2 { + return // Scope and subsequent fields are skipped. + } + if !e.isScopeRecursive { + e.scopeEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + } + // Collect Log field. + if e.fieldCount <= 3 { + return // Log and subsequent fields are skipped. + } + if !e.isLogRecursive { + e.logEncoder.CollectColumns(columnSet.At(colIdx)) + colIdx++ + } +} + +// LogsDecoder implements decoding of Logs +type LogsDecoder struct { + buf pkg.BitsReader + column *pkg.ReadableColumn + fieldCount uint + + envelopeDecoder *EnvelopeDecoder + isEnvelopeRecursive bool + resourceDecoder *ResourceDecoder + isResourceRecursive bool + scopeDecoder *ScopeDecoder + isScopeRecursive bool + logDecoder *LogRecordDecoder + isLogRecursive bool + allocators *Allocators +} + +// Init is called once in the lifetime of the stream. +func (d *LogsDecoder) Init(state *ReaderState, columns *pkg.ReadColumnSet) error { + // Remember this decoder in the state so that we can detect recursion. + if state.LogsDecoder != nil { + panic("cannot initialize LogsDecoder: already initialized") + } + state.LogsDecoder = d + defer func() { state.LogsDecoder = nil }() + + d.allocators = &state.Allocators + + // Number of fields in the input data schema. + var err error + d.fieldCount, err = state.StructFieldCounts.LogsFieldCount() + if err != nil { + return fmt.Errorf("cannot find struct %s in override schema: %w", "Logs", err) + } + if d.fieldCount > 4 { + return pkg.ErrTooManyFieldsToDecode + } + + if d.fieldCount == 0 { + return pkg.ErrEmptyRootStructDisallowed + } + + d.column = columns.Column() + + if d.fieldCount <= 0 { + return nil // Envelope and subsequent fields are skipped. + } + if state.EnvelopeDecoder != nil { + // Recursion detected, use the existing decoder. + d.envelopeDecoder = state.EnvelopeDecoder + d.isEnvelopeRecursive = true // Mark that we are using a recursive decoder. + } else { + d.envelopeDecoder = new(EnvelopeDecoder) + err = d.envelopeDecoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + if d.fieldCount <= 1 { + return nil // Resource and subsequent fields are skipped. + } + if state.ResourceDecoder != nil { + // Recursion detected, use the existing decoder. + d.resourceDecoder = state.ResourceDecoder + d.isResourceRecursive = true // Mark that we are using a recursive decoder. + } else { + d.resourceDecoder = new(ResourceDecoder) + err = d.resourceDecoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + if d.fieldCount <= 2 { + return nil // Scope and subsequent fields are skipped. + } + if state.ScopeDecoder != nil { + // Recursion detected, use the existing decoder. + d.scopeDecoder = state.ScopeDecoder + d.isScopeRecursive = true // Mark that we are using a recursive decoder. + } else { + d.scopeDecoder = new(ScopeDecoder) + err = d.scopeDecoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + if d.fieldCount <= 3 { + return nil // Log and subsequent fields are skipped. + } + if state.LogRecordDecoder != nil { + // Recursion detected, use the existing decoder. + d.logDecoder = state.LogRecordDecoder + d.isLogRecursive = true // Mark that we are using a recursive decoder. + } else { + d.logDecoder = new(LogRecordDecoder) + err = d.logDecoder.Init(state, columns.AddSubColumn()) + } + if err != nil { + return err + } + + return nil +} + +// Continue is called at the start of the frame to continue decoding column data. +// This should set the decoder's source buffer, so the new decoding continues from +// the supplied column data. This should NOT reset the internal state of the decoder, +// since columns can cross frame boundaries and the new column data is considered +// continuation of that same column in the previous frame. +func (d *LogsDecoder) Continue() { + d.buf.Reset(d.column.Data()) + + if d.fieldCount <= 0 { + return // Envelope and subsequent fields are skipped. + } + + if !d.isEnvelopeRecursive { + d.envelopeDecoder.Continue() + } + + if d.fieldCount <= 1 { + return // Resource and subsequent fields are skipped. + } + + if !d.isResourceRecursive { + d.resourceDecoder.Continue() + } + + if d.fieldCount <= 2 { + return // Scope and subsequent fields are skipped. + } + + if !d.isScopeRecursive { + d.scopeDecoder.Continue() + } + + if d.fieldCount <= 3 { + return // Log and subsequent fields are skipped. + } + + if !d.isLogRecursive { + d.logDecoder.Continue() + } + +} + +func (d *LogsDecoder) Reset() { + + if d.fieldCount <= 0 { + return // Envelope and all subsequent fields are skipped. + } + + if !d.isEnvelopeRecursive { + d.envelopeDecoder.Reset() + } + + if d.fieldCount <= 1 { + return // Resource and all subsequent fields are skipped. + } + + if !d.isResourceRecursive { + d.resourceDecoder.Reset() + } + + if d.fieldCount <= 2 { + return // Scope and all subsequent fields are skipped. + } + + if !d.isScopeRecursive { + d.scopeDecoder.Reset() + } + + if d.fieldCount <= 3 { + return // Log and all subsequent fields are skipped. + } + + if !d.isLogRecursive { + d.logDecoder.Reset() + } + +} + +func (d *LogsDecoder) Decode(dstPtr *Logs) error { + val := dstPtr + + // Read bits that indicate which fields follow. + val.modifiedFields.mask = d.buf.PeekBits(d.fieldCount) + d.buf.Consume(d.fieldCount) + + if val.modifiedFields.mask&fieldModifiedLogsEnvelope != 0 { // Envelope is changed. + + if err := d.envelopeDecoder.Decode(&val.envelope); err != nil { + return err + } + } + + if val.modifiedFields.mask&fieldModifiedLogsResource != 0 { // Resource is changed. + + if val.resource == nil { + if err := d.allocators.allocSizeChecker.PrepAllocSize(uint(unsafe.Sizeof(*val.resource))); err != nil { + return err + } + val.resource = d.allocators.Resource.Alloc() + val.resource.init(&val.modifiedFields, fieldModifiedLogsResource) + } + + if err := d.resourceDecoder.Decode(&val.resource); err != nil { + return err + } + if val.resource == nil { + return pkg.ErrDecodeError // Non-optional fields cannot be nil + } + } + + if val.modifiedFields.mask&fieldModifiedLogsScope != 0 { // Scope is changed. + + if val.scope == nil { + if err := d.allocators.allocSizeChecker.PrepAllocSize(uint(unsafe.Sizeof(*val.scope))); err != nil { + return err + } + val.scope = d.allocators.Scope.Alloc() + val.scope.init(&val.modifiedFields, fieldModifiedLogsScope) + } + + if err := d.scopeDecoder.Decode(&val.scope); err != nil { + return err + } + if val.scope == nil { + return pkg.ErrDecodeError // Non-optional fields cannot be nil + } + } + + if val.modifiedFields.mask&fieldModifiedLogsLog != 0 { // Log is changed. + + if err := d.logDecoder.Decode(&val.log); err != nil { + return err + } + } + + return d.buf.Error() +} + +// LogsAllocator implements a custom allocator for Logs. +// It maintains a pool of pre-allocated Logs and grows the pool +// dynamically as needed, up to a maximum size of 64 elements. +type LogsAllocator struct { + pool []Logs + ofs int +} + +// Alloc returns the next available Logs from the pool. +// If the pool is exhausted, it grows the pool by doubling its size +// up to a maximum of 64 elements. +func (a *LogsAllocator) Alloc() *Logs { + if a.ofs < len(a.pool) { + // Get the next available Logs from the pool + a.ofs++ + return &a.pool[a.ofs-1] + } + // We've exhausted the current pool, prealloc a new pool. + return a.prealloc() +} + +//go:noinline +func (a *LogsAllocator) prealloc() *Logs { + // prealloc expands the pool by doubling its size, up to a maximum of 64 elements. + // If the pool is empty, it starts with 1 element. + newLen := min(max(len(a.pool)*2, 1), 64) + a.pool = make([]Logs, newLen) + a.ofs = 1 + return &a.pool[0] +} + +var wireSchemaLogs = []byte{0x06, 0x04, 0x01, 0x03, 0x07, 0x05, 0x0B} + +func LogsWireSchema() (schema.WireSchema, error) { + var w schema.WireSchema + if err := w.Deserialize(bytes.NewReader(wireSchemaLogs)); err != nil { + return w, err + } + return w, nil +} diff --git a/go/otel/otelstef/logsreader.go b/go/otel/otelstef/logsreader.go new file mode 100644 index 00000000..d56818bc --- /dev/null +++ b/go/otel/otelstef/logsreader.go @@ -0,0 +1,140 @@ +// Code generated by stefc. DO NOT EDIT. +package otelstef + +import ( + "bufio" + "errors" + "fmt" + "io" + + "github.com/splunk/stef/go/pkg" + "github.com/splunk/stef/go/pkg/schema" +) + +type LogsReader struct { + // Record contains the record that was just read by the last Read() operation. + // Do not modify this field externally. The next Read() will overwrite the Record. + Record Logs + + base pkg.BaseReader + decoder LogsDecoder + state ReaderState + recordPtr *Logs +} + +func NewLogsReader(source io.Reader) (*LogsReader, error) { + bufferedSource := bufio.NewReaderSize(source, 64*1024) + reader := &LogsReader{} + + reader.Record.Init() + reader.recordPtr = &reader.Record + + if err := reader.base.Init(bufferedSource); err != nil { + return nil, err + } + + if err := reader.initSchema(); err != nil { + return nil, err + } + + return reader, nil +} + +func (r *LogsReader) initSchema() error { + ownSchema, err := LogsWireSchema() + if err != nil { + return err + } + if err := r.base.ReadVarHeader(ownSchema); err != nil { + return err + } + + r.state.Init(r.base.Schema) + err = r.decoder.Init(&r.state, &r.base.ReadBufs.Columns) + if err != nil { + return fmt.Errorf("schema is not compatible with Reader: %w", err) + } + + if !r.state.StructFieldCounts.AllFetched() { + return errors.New("override schema iterator is not done, decoded data schema is likely incompatible") + } + + return nil +} + +func (f *LogsReader) Header() *pkg.FixedHeader { + return &f.base.FixedHeader +} + +func (f *LogsReader) UserData() map[string]string { + return f.base.VarHeader.UserData +} + +// Read the next record. After Read() returns successfully the record +// will be accessible in LogsReader.Record field. +// +// If ReadOptions.TillEndOfFrame=true and end of the current +// frame is reached Read() will return pkg.ErrEndOfFrame. Calling Read() +// after that with ReadOptions.TillEndOfFrame=false will correctly load +// the next frame (if any) and read the next record from the frame. +// The very first Read() must use ReadOptions.TillEndOfFrame=false +// otherwise pkg.ErrEndOfFrame will be returned since no frames are +// initially loaded in the Reader. See more details in pkg.ReadOptions. +// +// If Read() encounters a decoding error one of the pkg.Err values will +// be returned. +// If underlying source io.Reader returns any error then Read() will +// either return that error or a decoding error if source io.Reader +// returned an error prematurely while more data was expected in STEF stream. +// +// For well-formed streams that don't encounter decoding errors Read() will +// return io.EOF once end of the underlying source io.Reader is reached +// (assuming io.Reader returns io.EOF itself). +func (r *LogsReader) Read(opts pkg.ReadOptions) error { + for r.base.FrameRecordCount == 0 { + if opts.TillEndOfFrame { + return pkg.ErrEndOfFrame + } + if err := r.nextFrame(); err != nil { + return err + } + } + r.base.FrameRecordCount-- + r.base.RecordCount++ + + // Track new memory allocated during decoding of this record. + r.state.Allocators.allocSizeChecker.ResetAllocSize() + + return r.decoder.Decode(r.recordPtr) +} + +func (r *LogsReader) RecordCount() uint64 { + return r.base.RecordCount +} + +func (r *LogsReader) nextFrame() error { + frameFlags, err := r.base.NextFrame() + if err != nil { + return err + } + + if frameFlags&pkg.RestartDictionaries != 0 { + // The frame that has just started indicates that the dictionaries + // must be restarted. Reset all dictionaries. + r.state.ResetDicts() + } + + if frameFlags&pkg.RestartCodecs != 0 { + // The frame that has just started indicates that the decoders + // must be restarted. + r.decoder.Reset() + } + + r.decoder.Continue() + return nil +} + +// Schema returns the schema of the STEF stream being read. +func (r *LogsReader) Schema() *schema.WireSchema { + return r.base.Schema +} diff --git a/go/otel/otelstef/logswriter.go b/go/otel/otelstef/logswriter.go new file mode 100644 index 00000000..c4da3fd1 --- /dev/null +++ b/go/otel/otelstef/logswriter.go @@ -0,0 +1,210 @@ +// Code generated by stefc. DO NOT EDIT. +package otelstef + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + + "github.com/splunk/stef/go/pkg" +) + +type LogsWriter struct { + Record Logs + + opts pkg.WriterOptions + dst pkg.ChunkWriter + frameEncoder pkg.FrameEncoder + encoder LogsEncoder + state WriterState + writeBufs pkg.WriteBufs + frameRecordCount uint64 + recordCount uint64 +} + +func NewLogsWriter(dst pkg.ChunkWriter, opts pkg.WriterOptions) (*LogsWriter, error) { + writer := &LogsWriter{ + dst: dst, + opts: opts, + } + + // Set default options. + if writer.opts.MaxUncompressedFrameByteSize == 0 { + writer.opts.MaxUncompressedFrameByteSize = pkg.DefaultMaxFrameSize + } + if writer.opts.MaxTotalDictSize == 0 { + writer.opts.MaxTotalDictSize = pkg.DefaultMaxTotalDictSize + } + + if writer.opts.Schema != nil { + // If the schema is overridden must include the descriptor so that the readers + // can decode the data correctly. + writer.opts.IncludeDescriptor = true + + ownSchema, err := LogsWireSchema() + if err != nil { + return nil, err + } + if _, err := ownSchema.Compatible(writer.opts.Schema); err != nil { + return nil, fmt.Errorf("schema is not compatible with Writer: %w", err) + } + } + + writer.Record.Init() + writer.state.Init(&writer.opts) + if err := writer.encoder.Init(&writer.state, &writer.writeBufs.Columns); err != nil { + return nil, err + } + + if !writer.state.StructFieldCounts.AllFetched() { + return nil, errors.New("override schema iterator is not done, likely supplied schema override does not match the generated code") + } + + if err := writer.frameEncoder.Init(dst, writer.opts.Compression); err != nil { + return nil, err + } + + if err := writer.writeFixedHeader(); err != nil { + return nil, err + } + + if err := writer.writeVarHeader(); err != nil { + return nil, err + } + + return writer, nil +} + +func (f *LogsWriter) writeFixedHeader() error { + var hdrTail []byte + + var versionAndType byte = pkg.HdrFormatVersion + hdrTail = append(hdrTail, versionAndType) + + flags := byte(f.opts.Compression) & pkg.HdrFlagsCompressionMethod + + hdrTail = append(hdrTail, flags) + hdrTailSize := uint64(len(hdrTail)) + + var hdrFull []byte + hdrFull = append(hdrFull, pkg.HdrSignature...) + hdrFull = binary.AppendUvarint(hdrFull, hdrTailSize) + hdrFull = append(hdrFull, hdrTail...) + + err := f.dst.WriteChunk(hdrFull, nil) + return err +} + +func (w *LogsWriter) writeVarHeader() error { + // Serialize varheader + hdr := pkg.VarHeader{} + if w.opts.IncludeDescriptor { + if w.opts.Schema != nil { + var buf bytes.Buffer + err := w.opts.Schema.Serialize(&buf) + if err != nil { + return fmt.Errorf("could not marshal schema: %w", err) + } + hdr.SchemaWireBytes = buf.Bytes() + } else { + hdr.SchemaWireBytes = []byte(wireSchemaLogs) + } + } + + if len(w.opts.UserData) > 0 { + hdr.UserData = w.opts.UserData + } + + var buf bytes.Buffer + err := hdr.Serialize(&buf) + if err != nil { + return err + } + + // Write to the frame + _, err = w.frameEncoder.Write(buf.Bytes()) + if err != nil { + return err + } + + // Close the the frame. + if err := w.frameEncoder.CloseFrame(); err != nil { + return err + } + + // Open a new frame. + w.frameEncoder.OpenFrame(0) + + return nil +} + +func (w *LogsWriter) Write() error { + w.encoder.Encode(&w.Record) + w.frameRecordCount++ + + nextFrameFlags := w.opts.FrameRestartFlags + restartFrame := false + if w.state.limiter.DictLimitReached() || (nextFrameFlags&pkg.RestartDictionaries != 0) { + w.state.ResetDicts() + nextFrameFlags = w.opts.FrameRestartFlags | pkg.RestartDictionaries + restartFrame = true + } + + if w.state.limiter.FrameLimitReached() { + restartFrame = true + } + + if restartFrame { + if err := w.restartFrame(nextFrameFlags); err != nil { + return err + } + } + + w.recordCount++ + + return nil +} + +func (w *LogsWriter) RecordCount() uint64 { + return w.recordCount +} + +func (w *LogsWriter) restartFrame(nextFrameFlags pkg.FrameFlags) error { + if nextFrameFlags&pkg.RestartCodecs != 0 { + w.encoder.Reset() + } + + // Write record count. + if _, err := w.frameEncoder.Write(binary.AppendUvarint(nil, w.frameRecordCount)); err != nil { + return err + } + w.frameRecordCount = 0 + + // Write from column buffers to the frame buffer. + w.encoder.CollectColumns(&w.writeBufs.Columns) + if err := w.writeBufs.WriteTo(&w.frameEncoder); err != nil { + return err + } + + // Close the current frame. + if err := w.frameEncoder.CloseFrame(); err != nil { + return err + } + + // Open a new frame. + w.frameEncoder.OpenFrame(nextFrameFlags) + + // Reset the frame size limiter, this is a new frame. + w.state.limiter.ResetFrameSize() + + return nil +} + +func (w *LogsWriter) Flush() error { + if w.frameRecordCount == 0 { + // Nothing to flush. + return nil + } + return w.restartFrame(w.opts.FrameRestartFlags) +} diff --git a/go/otel/otelstef/logswriter_test.go b/go/otel/otelstef/logswriter_test.go new file mode 100644 index 00000000..56290b2b --- /dev/null +++ b/go/otel/otelstef/logswriter_test.go @@ -0,0 +1,251 @@ +// Code generated by stefc. DO NOT EDIT. +package otelstef + +import ( + "bytes" + "fmt" + "io" + "math/rand/v2" + "os" + "strconv" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/splunk/stef/go/pkg" + "github.com/splunk/stef/go/pkg/idl" + "github.com/splunk/stef/go/pkg/schema" +) + +// genLogsRecords generates a number of records pseudo-randomly +// using the supplied Rand generator. Generated records will be always +// the same for the same input state of Rand generator. +// Generated records will only have fields set (mutated) that are defined +// in the supplied schema. This allows testing schema evolution. +func genLogsRecords(random *rand.Rand, schem *schema.Schema) (records []Logs) { + const recCount = 1000 + var record Logs + record.Init() + + records = make([]Logs, recCount) + for i := 0; i < recCount; i++ { + limiter := &mutateRandomLimiter{} + record.mutateRandom(random, schem, limiter) + records[i].Init() + records[i].CopyFrom(&record) + } + + return records +} + +func testLogsWriteReadSeed(t *testing.T, seed uint64) (retVal bool) { + retVal = true + + random := rand.New(rand.NewPCG(seed, 0)) + + // Load the schema from the allSchemaContent variable. + schem, err := idl.Parse([]byte(allSchemaContent), "") + require.NoError(t, err, "seed %v", seed) + + schem, err = schem.PrunedForRoot("Logs") + require.NoError(t, err, "seed %v", seed) + + if random.IntN(2) == 0 { + // Randomly shrink the schema in approximately half of the test runs. + // This is to test that the writer/reader can handle schema changes. + schema.ShrinkRandomly(random, schem) + } + wireSchema := schema.NewWireSchema(schem, "Logs") + + for _, opt := range testWriterOpts { + t.Run( + "", func(t *testing.T) { + succeeded := false + defer func() { + if !succeeded { + retVal = false + fmt.Printf("Test failed with seed %v\n", seed) + } + }() + + // Write data according to (possibly modified) schema + optCpy := opt + optCpy.Schema = &wireSchema + + buf := &pkg.MemChunkWriter{} + writer, err := NewLogsWriter(buf, optCpy) + require.NoError(t, err, "seed %v", seed) + + // Generate records pseudo-randomly + records := genLogsRecords(random, schem) + // Write the records + for i := 0; i < len(records); i++ { + writer.Record.CopyFrom(&records[i]) + err = writer.Write() + require.NoError(t, err, "record %d seed %v", i, seed) + } + err = writer.Flush() + require.NoError(t, err, "seed %v", seed) + + // Read the records and compare to written. + reader, err := NewLogsReader(bytes.NewBuffer(buf.Bytes())) + require.NoError(t, err, "seed %v", seed) + + for i := 0; i < len(records); i++ { + err := reader.Read(pkg.ReadOptions{}) + require.NoError(t, err, "record %d seed %v", i, seed) + require.True(t, reader.Record.IsEqual(&records[i]), "record %d seed %v", i, seed) + } + err = reader.Read(pkg.ReadOptions{}) + require.ErrorIs(t, err, io.EOF, seed) + + succeeded = true + }, + ) + } + return retVal +} + +func TestLogsWriteRead(t *testing.T) { + seedFileName := "../../../seeds/otelstef_Logs_seeds.txt" + seedsBytes, err := os.ReadFile(seedFileName) + var seeds []string + if err == nil { + seeds = strings.Split(strings.TrimSpace(string(seedsBytes)), "\n") + } + + for _, seedStr := range seeds { + seed, err := strconv.ParseUint(seedStr, 10, 64) + fmt.Printf("Testing with seed from file: %v\n", seed) + require.NoError(t, err, "parsing seed from file "+seedFileName) + testLogsWriteReadSeed(t, seed) + } + + // Choose a seed (non-pseudo) randomly. We will print the seed + // on failure for easy reproduction. + seed := uint64(time.Now().UnixNano()) + + succeeded := false + defer func() { + if !succeeded { + fmt.Printf("Test failed with seed %v, adding to seed file\n", seed) + err := os.WriteFile(seedFileName, + []byte(string(seedsBytes)+fmt.Sprintf("%v\n", seed)), 0644) + require.NoError(t, err) + } + }() + + succeeded = testLogsWriteReadSeed(t, seed) +} + +func TestLogsWriteReadLong(t *testing.T) { + // Tests writing and reading a larger number of records + // in a single writer/reader session. + + seed := uint64(time.Now().UnixNano()) + random := rand.New(rand.NewPCG(seed, 0)) + + schem, err := idl.Parse([]byte(allSchemaContent), "") + require.NoError(t, err, "seed %v", seed) + + schem, err = schem.PrunedForRoot("Logs") + require.NoError(t, err, "seed %v", seed) + + mem := &pkg.MemReaderWriter{} + + writer, err := NewLogsWriter(mem, pkg.WriterOptions{Compression: pkg.CompressionZstd}) + require.NoError(t, err, "seed %v", seed) + + reader, err := NewLogsReader(mem) + require.NoError(t, err, "seed %v", seed) + + iterations := 10 + if os.Getenv("STEF_ENABLE_SLOW_TESTS") == "1" { + iterations = 100 + } + + var record Logs + record.Init() + + records := make([]Logs, 1000) + for i := 0; i < iterations; i++ { + recCount := 1 + random.IntN(1000) + records = records[:recCount] + + // Write some records. + for recIdx := range records { + limiter := &mutateRandomLimiter{} + record.mutateRandom(random, schem, limiter) + records[recIdx] = Logs{} + records[recIdx].Init() + records[recIdx].CopyFrom(&record) + writer.Record.CopyFrom(&record) + err = writer.Write() + require.NoError(t, err, "record %d:%d seed %v", i, recIdx, seed) + } + + // Flush to make sure the records are in the MemReaderWriter and can be + // read back. + err = writer.Flush() + require.NoError(t, err, "iteration %d seed %v", i, seed) + + // Read the records back and compare. + for recIdx := range records { + err = reader.Read(pkg.ReadOptions{}) + require.NoError(t, err, "record %d:%d seed %v", i, recIdx, seed) + require.True(t, reader.Record.IsEqual(&records[recIdx]), "record %d:%d seed %v", i, recIdx, seed) + } + } + + err = reader.Read(pkg.ReadOptions{}) + require.ErrorIs(t, err, io.EOF, seed) +} + +func FuzzLogsReader(f *testing.F) { + f.Add([]byte("")) + + random := rand.New(rand.NewPCG(0, 0)) + schem, err := idl.Parse([]byte(allSchemaContent), "") + require.NoError(f, err) + + for _, opt := range testWriterOpts { + for i := 0; i <= 3; i++ { + buf := &pkg.MemChunkWriter{} + writer, err := NewLogsWriter(buf, opt) + require.NoError(f, err) + + recCount := (1 << (2 * i)) - 1 + var record Logs + record.Init() + for range recCount { + limiter := &mutateRandomLimiter{} + record.mutateRandom(random, schem, limiter) + writer.Record.CopyFrom(&record) + err = writer.Write() + require.NoError(f, err) + } + + err = writer.Flush() + require.NoError(f, err) + + f.Add(buf.Bytes()) + } + } + + f.Fuzz( + func(t *testing.T, data []byte) { + reader, err := NewLogsReader(bytes.NewBuffer(data)) + if err != nil { + return + } + for { + err = reader.Read(pkg.ReadOptions{}) + if err != nil { + break + } + } + }, + ) +} diff --git a/go/otel/otelstef/metric.go b/go/otel/otelstef/metric.go index 171db392..aaa0a98f 100644 --- a/go/otel/otelstef/metric.go +++ b/go/otel/otelstef/metric.go @@ -116,6 +116,14 @@ func (s *Metric) fixParent(parentModifiedFields *modifiedFields) { s.histogramBounds.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Metric) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.metadata.attachParent(&s.modifiedFields, fieldModifiedMetricMetadata) + s.histogramBounds.attachParent(&s.modifiedFields, fieldModifiedMetricHistogramBounds) +} + // Freeze the struct. Any attempt to modify it after this will panic. // This marks the struct as eligible for safely sharing by pointer without cloning, // which can improve encoding performance. @@ -336,6 +344,14 @@ func (s *Metric) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Metric) clearModifiedRecursively() { + s.metadata.clearModifiedRecursively() + s.histogramBounds.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Metric) computeDiff(val *Metric) (ret bool) { @@ -387,6 +403,14 @@ func (s *Metric) canBeShared() bool { return s.isFrozen() } +// clone returns a clone of s allocated using allocators. +func (s *Metric) clone(allocators *Allocators) *Metric { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Metric{}))) + c := allocators.Metric.Alloc() + s.CloneTo(c, allocators) + return c +} + // cloneShared returns a clone of s. It may return s if it is safe to share without cloning // (for example if s is frozen). func (s *Metric) cloneShared(allocators *Allocators) *Metric { @@ -394,13 +418,13 @@ func (s *Metric) cloneShared(allocators *Allocators) *Metric { // If s is frozen it means it is safe to share without cloning. return s } - return s.Clone(allocators) + return s.clone(allocators) } -func (s *Metric) Clone(allocators *Allocators) *Metric { - allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Metric{}))) - c := allocators.Metric.Alloc() - *c = Metric{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Metric) CloneTo(dst *Metric, allocators *Allocators) { + *dst = Metric{ name: s.name, description: s.description, unit: s.unit, @@ -408,9 +432,10 @@ func (s *Metric) Clone(allocators *Allocators) *Metric { aggregationTemporality: s.aggregationTemporality, monotonic: s.monotonic, } - copyToNewAttributes(&c.metadata, &s.metadata, allocators) - copyToNewFloat64Array(&c.histogramBounds, &s.histogramBounds, allocators) - return c + copyToNewAttributes(&dst.metadata, &s.metadata, allocators) + copyToNewFloat64Array(&dst.histogramBounds, &s.histogramBounds, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -674,7 +699,7 @@ func (d *MetricEncoderDict) Add(val *Metric) { d.dict.Set(val, refNum) return } - clone := val.Clone(d.allocators) // Clone before adding to dictionary. + clone := val.clone(d.allocators) // Clone before adding to dictionary. clone.Freeze() // Freeze the clone so that it can be safely shared by pointer. d.dict.Set(clone, refNum) } @@ -1224,7 +1249,7 @@ func (d *MetricDecoder) Decode(dstPtr **Metric) error { // *dstPtr is pointing to a element in the dictionary. We are not allowed // to modify it. Make a clone of it and decode into the clone. - val := (*dstPtr).Clone(d.allocators) + val := (*dstPtr).clone(d.allocators) if d.allocators.allocSizeChecker.IsOverLimit() { return pkg.ErrRecordAllocLimitExceeded } diff --git a/go/otel/otelstef/metrics.go b/go/otel/otelstef/metrics.go index b86f5934..d240161b 100644 --- a/go/otel/otelstef/metrics.go +++ b/go/otel/otelstef/metrics.go @@ -119,6 +119,24 @@ func (s *Metrics) fixParent(parentModifiedFields *modifiedFields) { s.point.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Metrics) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.envelope.attachParent(&s.modifiedFields, fieldModifiedMetricsEnvelope) + if !s.metric.canBeShared() { + s.metric.attachParent(&s.modifiedFields, fieldModifiedMetricsMetric) + } + if !s.resource.canBeShared() { + s.resource.attachParent(&s.modifiedFields, fieldModifiedMetricsResource) + } + if !s.scope.canBeShared() { + s.scope.attachParent(&s.modifiedFields, fieldModifiedMetricsScope) + } + s.attributes.attachParent(&s.modifiedFields, fieldModifiedMetricsAttributes) + s.point.attachParent(&s.modifiedFields, fieldModifiedMetricsPoint) +} + func (s *Metrics) freeze() { if s.isFrozen() { return @@ -168,7 +186,7 @@ func (s *Metrics) SetMetric(v *Metric) { } } else { if s.metric.canBeShared() { - s.metric = s.metric.Clone(&Allocators{}) + s.metric = s.metric.clone(&Allocators{}) } s.metric.CopyFrom(v) s.modifiedFields.markModified(fieldModifiedMetricsMetric) @@ -203,7 +221,7 @@ func (s *Metrics) SetResource(v *Resource) { } } else { if s.resource.canBeShared() { - s.resource = s.resource.Clone(&Allocators{}) + s.resource = s.resource.clone(&Allocators{}) } s.resource.CopyFrom(v) s.modifiedFields.markModified(fieldModifiedMetricsResource) @@ -238,7 +256,7 @@ func (s *Metrics) SetScope(v *Scope) { } } else { if s.scope.canBeShared() { - s.scope = s.scope.Clone(&Allocators{}) + s.scope = s.scope.clone(&Allocators{}) } s.scope.CopyFrom(v) s.modifiedFields.markModified(fieldModifiedMetricsScope) @@ -327,6 +345,24 @@ func (s *Metrics) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Metrics) clearModifiedRecursively() { + s.envelope.clearModifiedRecursively() + if !s.metric.canBeShared() { + s.metric.clearModifiedRecursively() + } + if !s.resource.canBeShared() { + s.resource.clearModifiedRecursively() + } + if !s.scope.canBeShared() { + s.scope.clearModifiedRecursively() + } + s.attributes.clearModifiedRecursively() + s.point.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Metrics) computeDiff(val *Metrics) (ret bool) { @@ -368,22 +404,18 @@ func (s *Metrics) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Metrics) cloneShared(allocators *Allocators) Metrics { - return s.Clone(allocators) -} - -func (s *Metrics) Clone(allocators *Allocators) Metrics { - c := Metrics{ - metric: s.metric.cloneShared(allocators), - resource: s.resource.cloneShared(allocators), - scope: s.scope.cloneShared(allocators), - } - copyToNewEnvelope(&c.envelope, &s.envelope, allocators) - copyToNewAttributes(&c.attributes, &s.attributes, allocators) - copyToNewPoint(&c.point, &s.point, allocators) - return c +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Metrics) CloneTo(dst *Metrics, allocators *Allocators) { + *dst = Metrics{} + copyToNewEnvelope(&dst.envelope, &s.envelope, allocators) + dst.metric = s.metric.cloneShared(allocators) + dst.resource = s.resource.cloneShared(allocators) + dst.scope = s.scope.cloneShared(allocators) + copyToNewAttributes(&dst.attributes, &s.attributes, allocators) + copyToNewPoint(&dst.point, &s.point, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -515,12 +547,12 @@ func (s *Metrics) mutateRandom(random *rand.Rand, schem *schema.Schema, limiter s.metric = new(Metric) s.metric.init(&s.modifiedFields, fieldModifiedMetricsMetric) } else { - s.metric = s.metric.Clone(&Allocators{}) + s.metric = s.metric.clone(&Allocators{}) } } if s.metric.canBeShared() { // metric may be shared by pointer. Clone it to have exclusive ownership. - s.metric = s.metric.Clone(&Allocators{}) + s.metric = s.metric.clone(&Allocators{}) } s.metric.mutateRandom(random, schem, limiter) @@ -539,12 +571,12 @@ func (s *Metrics) mutateRandom(random *rand.Rand, schem *schema.Schema, limiter s.resource = new(Resource) s.resource.init(&s.modifiedFields, fieldModifiedMetricsResource) } else { - s.resource = s.resource.Clone(&Allocators{}) + s.resource = s.resource.clone(&Allocators{}) } } if s.resource.canBeShared() { // resource may be shared by pointer. Clone it to have exclusive ownership. - s.resource = s.resource.Clone(&Allocators{}) + s.resource = s.resource.clone(&Allocators{}) } s.resource.mutateRandom(random, schem, limiter) @@ -563,12 +595,12 @@ func (s *Metrics) mutateRandom(random *rand.Rand, schem *schema.Schema, limiter s.scope = new(Scope) s.scope.init(&s.modifiedFields, fieldModifiedMetricsScope) } else { - s.scope = s.scope.Clone(&Allocators{}) + s.scope = s.scope.clone(&Allocators{}) } } if s.scope.canBeShared() { // scope may be shared by pointer. Clone it to have exclusive ownership. - s.scope = s.scope.Clone(&Allocators{}) + s.scope = s.scope.clone(&Allocators{}) } s.scope.mutateRandom(random, schem, limiter) diff --git a/go/otel/otelstef/modifiedfields.go b/go/otel/otelstef/modifiedfields.go index 15301bce..e0abea28 100644 --- a/go/otel/otelstef/modifiedfields.go +++ b/go/otel/otelstef/modifiedfields.go @@ -115,6 +115,13 @@ func (m *modifiedFieldsMultimap) fixParent(parentModifiedFields *modifiedFields) m.vals.parent = parentModifiedFields } +func (m *modifiedFieldsMultimap) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.keys.parent = parentModifiedFields + m.vals.parent = parentModifiedFields + m.keys.parentBit = parentModifiedBit + m.vals.parentBit = parentModifiedBit +} + // Returns a bitmask to be set in keys or vals to mark modification of // multimap element at index. func (m *modifiedFieldsMultimap) maskForIndex(index int) uint64 { diff --git a/go/otel/otelstef/point.go b/go/otel/otelstef/point.go index bfc50857..3c29c5df 100644 --- a/go/otel/otelstef/point.go +++ b/go/otel/otelstef/point.go @@ -92,6 +92,14 @@ func (s *Point) fixParent(parentModifiedFields *modifiedFields) { s.exemplars.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Point) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.value.attachParent(&s.modifiedFields, fieldModifiedPointValue) + s.exemplars.attachParent(&s.modifiedFields, fieldModifiedPointExemplars) +} + func (s *Point) freeze() { if s.isFrozen() { return @@ -205,6 +213,14 @@ func (s *Point) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Point) clearModifiedRecursively() { + s.value.clearModifiedRecursively() + s.exemplars.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Point) computeDiff(val *Point) (ret bool) { @@ -236,20 +252,17 @@ func (s *Point) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Point) cloneShared(allocators *Allocators) Point { - return s.Clone(allocators) -} - -func (s *Point) Clone(allocators *Allocators) Point { - c := Point{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Point) CloneTo(dst *Point, allocators *Allocators) { + *dst = Point{ startTimestamp: s.startTimestamp, timestamp: s.timestamp, } - copyToNewPointValue(&c.value, &s.value, allocators) - copyToNewExemplarArray(&c.exemplars, &s.exemplars, allocators) - return c + copyToNewPointValue(&dst.value, &s.value, allocators) + copyToNewExemplarArray(&dst.exemplars, &s.exemplars, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/pointvalue.go b/go/otel/otelstef/pointvalue.go index 2d6a71f5..a221545b 100644 --- a/go/otel/otelstef/pointvalue.go +++ b/go/otel/otelstef/pointvalue.go @@ -86,6 +86,21 @@ func (s *PointValue) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointer and bit in a newly copied oneof. +func (s *PointValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.parentModifiedFields = parentModifiedFields + s.parentModifiedBit = parentModifiedBit + + switch s.Type() { + case PointValueTypeHistogram: + s.histogramPtr().attachParent(parentModifiedFields, parentModifiedBit) + case PointValueTypeExpHistogram: + s.expHistogramPtr().attachParent(parentModifiedFields, parentModifiedBit) + case PointValueTypeSummary: + s.summaryPtr().attachParent(parentModifiedFields, parentModifiedBit) + } +} + type PointValueType byte const ( @@ -253,33 +268,31 @@ func (s *PointValue) canBeShared() bool { return false } -func (s *PointValue) cloneShared(allocators *Allocators) PointValue { - // Oneof is not shareable, so cloneShared is just a Clone. - return s.Clone(allocators) -} - -func (s *PointValue) Clone(allocators *Allocators) PointValue { - c := PointValue{} - c.clearValSetType(s.Type()) +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *PointValue) CloneTo(dst *PointValue, allocators *Allocators) { + *dst = PointValue{} + dst.clearValSetType(s.Type()) switch s.Type() { case PointValueTypeInt64: - *c.int64Ptr() = *s.int64Ptr() + *dst.int64Ptr() = *s.int64Ptr() case PointValueTypeFloat64: - *c.float64Ptr() = *s.float64Ptr() + *dst.float64Ptr() = *s.float64Ptr() case PointValueTypeHistogram: allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(HistogramValue{}))) - c.allocHistogramAlloc(allocators) - copyToNewHistogramValue(c.histogramPtr(), s.histogramPtr(), allocators) + dst.allocHistogramAlloc(allocators) + copyToNewHistogramValue(dst.histogramPtr(), s.histogramPtr(), allocators) case PointValueTypeExpHistogram: allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(ExpHistogramValue{}))) - c.allocExpHistogramAlloc(allocators) - copyToNewExpHistogramValue(c.expHistogramPtr(), s.expHistogramPtr(), allocators) + dst.allocExpHistogramAlloc(allocators) + copyToNewExpHistogramValue(dst.expHistogramPtr(), s.expHistogramPtr(), allocators) case PointValueTypeSummary: allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(SummaryValue{}))) - c.allocSummaryAlloc(allocators) - copyToNewSummaryValue(c.summaryPtr(), s.summaryPtr(), allocators) + dst.allocSummaryAlloc(allocators) + copyToNewSummaryValue(dst.summaryPtr(), s.summaryPtr(), allocators) } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -386,6 +399,18 @@ func (s *PointValue) setUnmodifiedRecursively() { } } +// clearModifiedRecursively clears modification state in all mutable descendants. +func (s *PointValue) clearModifiedRecursively() { + switch s.Type() { + case PointValueTypeHistogram: + s.histogramPtr().clearModifiedRecursively() + case PointValueTypeExpHistogram: + s.expHistogramPtr().clearModifiedRecursively() + case PointValueTypeSummary: + s.summaryPtr().clearModifiedRecursively() + } +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *PointValue) computeDiff(val *PointValue) (ret bool) { diff --git a/go/otel/otelstef/quantilevalue.go b/go/otel/otelstef/quantilevalue.go index b684b99e..09826fcd 100644 --- a/go/otel/otelstef/quantilevalue.go +++ b/go/otel/otelstef/quantilevalue.go @@ -80,6 +80,12 @@ func (s *QuantileValue) fixParent(parentModifiedFields *modifiedFields) { s.modifiedFields.parent = parentModifiedFields } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *QuantileValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit +} + func (s *QuantileValue) freeze() { s.modifiedFields.freeze() } @@ -146,6 +152,12 @@ func (s *QuantileValue) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *QuantileValue) clearModifiedRecursively() { + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *QuantileValue) computeDiff(val *QuantileValue) (ret bool) { @@ -167,18 +179,15 @@ func (s *QuantileValue) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *QuantileValue) cloneShared(allocators *Allocators) QuantileValue { - return s.Clone(allocators) -} - -func (s *QuantileValue) Clone(allocators *Allocators) QuantileValue { - c := QuantileValue{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *QuantileValue) CloneTo(dst *QuantileValue, allocators *Allocators) { + *dst = QuantileValue{ quantile: s.quantile, value: s.value, } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/quantilevaluearray.go b/go/otel/otelstef/quantilevaluearray.go index 0ef85e73..e12d8ca3 100644 --- a/go/otel/otelstef/quantilevaluearray.go +++ b/go/otel/otelstef/quantilevaluearray.go @@ -52,6 +52,15 @@ func (e *QuantileValueArray) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *QuantileValueArray) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit + for i := range e.elems { + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + } +} + func (e *QuantileValueArray) canBeShared() bool { // An array can never be shared. return false @@ -96,6 +105,14 @@ func (e *QuantileValueArray) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *QuantileValueArray) clearModifiedRecursively() { + for i := 0; i < len(e.elems); i++ { + e.elems[i].clearModifiedRecursively() + } + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *QuantileValueArray) computeDiff(val *QuantileValueArray) (ret bool) { diff --git a/go/otel/otelstef/readerstate.go b/go/otel/otelstef/readerstate.go index 967697fe..9091e8c7 100644 --- a/go/otel/otelstef/readerstate.go +++ b/go/otel/otelstef/readerstate.go @@ -14,6 +14,8 @@ type ReaderState struct { // Dictionaries AnyValueString codecs.StringDictDecoderDict AttributeKey codecs.StringDictDecoderDict + LogEventName codecs.StringDictDecoderDict + LogSeverityText codecs.StringDictDecoderDict Metric MetricDecoderDict MetricDescription codecs.StringDictDecoderDict MetricName codecs.StringDictDecoderDict @@ -44,6 +46,8 @@ type ReaderState struct { KeyValueListDecoder *KeyValueListDecoder LinkDecoder *LinkDecoder LinkArrayDecoder *LinkArrayDecoder + LogRecordDecoder *LogRecordDecoder + LogsDecoder *LogsDecoder MetricDecoder *MetricDecoder MetricsDecoder *MetricsDecoder PointDecoder *PointDecoder @@ -66,6 +70,8 @@ func (d *ReaderState) Init(overrideSchema *schema.WireSchema) { d.AnyValueString.Init() d.AttributeKey.Init() + d.LogEventName.Init() + d.LogSeverityText.Init() d.Metric.Init() d.MetricDescription.Init() d.MetricName.Init() @@ -84,6 +90,8 @@ func (d *ReaderState) Init(overrideSchema *schema.WireSchema) { func (d *ReaderState) ResetDicts() { d.AnyValueString.Reset() d.AttributeKey.Reset() + d.LogEventName.Reset() + d.LogSeverityText.Reset() d.Metric.Reset() d.MetricDescription.Reset() d.MetricName.Reset() diff --git a/go/otel/otelstef/resource.go b/go/otel/otelstef/resource.go index 89b0131a..e5251da0 100644 --- a/go/otel/otelstef/resource.go +++ b/go/otel/otelstef/resource.go @@ -98,6 +98,13 @@ func (s *Resource) fixParent(parentModifiedFields *modifiedFields) { s.attributes.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Resource) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.attributes.attachParent(&s.modifiedFields, fieldModifiedResourceAttributes) +} + // Freeze the struct. Any attempt to modify it after this will panic. // This marks the struct as eligible for safely sharing by pointer without cloning, // which can improve encoding performance. @@ -196,6 +203,13 @@ func (s *Resource) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Resource) clearModifiedRecursively() { + s.attributes.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Resource) computeDiff(val *Resource) (ret bool) { @@ -222,6 +236,14 @@ func (s *Resource) canBeShared() bool { return s.isFrozen() } +// clone returns a clone of s allocated using allocators. +func (s *Resource) clone(allocators *Allocators) *Resource { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Resource{}))) + c := allocators.Resource.Alloc() + s.CloneTo(c, allocators) + return c +} + // cloneShared returns a clone of s. It may return s if it is safe to share without cloning // (for example if s is frozen). func (s *Resource) cloneShared(allocators *Allocators) *Resource { @@ -229,18 +251,19 @@ func (s *Resource) cloneShared(allocators *Allocators) *Resource { // If s is frozen it means it is safe to share without cloning. return s } - return s.Clone(allocators) + return s.clone(allocators) } -func (s *Resource) Clone(allocators *Allocators) *Resource { - allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Resource{}))) - c := allocators.Resource.Alloc() - *c = Resource{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Resource) CloneTo(dst *Resource, allocators *Allocators) { + *dst = Resource{ schemaURL: s.schemaURL, droppedAttributesCount: s.droppedAttributesCount, } - copyToNewAttributes(&c.attributes, &s.attributes, allocators) - return c + copyToNewAttributes(&dst.attributes, &s.attributes, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -413,7 +436,7 @@ func (d *ResourceEncoderDict) Add(val *Resource) { d.dict.Set(val, refNum) return } - clone := val.Clone(d.allocators) // Clone before adding to dictionary. + clone := val.clone(d.allocators) // Clone before adding to dictionary. clone.Freeze() // Freeze the clone so that it can be safely shared by pointer. d.dict.Set(clone, refNum) } @@ -732,7 +755,7 @@ func (d *ResourceDecoder) Decode(dstPtr **Resource) error { // *dstPtr is pointing to a element in the dictionary. We are not allowed // to modify it. Make a clone of it and decode into the clone. - val := (*dstPtr).Clone(d.allocators) + val := (*dstPtr).clone(d.allocators) if d.allocators.allocSizeChecker.IsOverLimit() { return pkg.ErrRecordAllocLimitExceeded } diff --git a/go/otel/otelstef/scope.go b/go/otel/otelstef/scope.go index 3b6e4315..4a595957 100644 --- a/go/otel/otelstef/scope.go +++ b/go/otel/otelstef/scope.go @@ -104,6 +104,13 @@ func (s *Scope) fixParent(parentModifiedFields *modifiedFields) { s.attributes.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Scope) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.attributes.attachParent(&s.modifiedFields, fieldModifiedScopeAttributes) +} + // Freeze the struct. Any attempt to modify it after this will panic. // This marks the struct as eligible for safely sharing by pointer without cloning, // which can improve encoding performance. @@ -252,6 +259,13 @@ func (s *Scope) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Scope) clearModifiedRecursively() { + s.attributes.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Scope) computeDiff(val *Scope) (ret bool) { @@ -288,6 +302,14 @@ func (s *Scope) canBeShared() bool { return s.isFrozen() } +// clone returns a clone of s allocated using allocators. +func (s *Scope) clone(allocators *Allocators) *Scope { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Scope{}))) + c := allocators.Scope.Alloc() + s.CloneTo(c, allocators) + return c +} + // cloneShared returns a clone of s. It may return s if it is safe to share without cloning // (for example if s is frozen). func (s *Scope) cloneShared(allocators *Allocators) *Scope { @@ -295,20 +317,21 @@ func (s *Scope) cloneShared(allocators *Allocators) *Scope { // If s is frozen it means it is safe to share without cloning. return s } - return s.Clone(allocators) + return s.clone(allocators) } -func (s *Scope) Clone(allocators *Allocators) *Scope { - allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof(Scope{}))) - c := allocators.Scope.Alloc() - *c = Scope{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Scope) CloneTo(dst *Scope, allocators *Allocators) { + *dst = Scope{ name: s.name, version: s.version, schemaURL: s.schemaURL, droppedAttributesCount: s.droppedAttributesCount, } - copyToNewAttributes(&c.attributes, &s.attributes, allocators) - return c + copyToNewAttributes(&dst.attributes, &s.attributes, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -517,7 +540,7 @@ func (d *ScopeEncoderDict) Add(val *Scope) { d.dict.Set(val, refNum) return } - clone := val.Clone(d.allocators) // Clone before adding to dictionary. + clone := val.clone(d.allocators) // Clone before adding to dictionary. clone.Freeze() // Freeze the clone so that it can be safely shared by pointer. d.dict.Set(clone, refNum) } @@ -918,7 +941,7 @@ func (d *ScopeDecoder) Decode(dstPtr **Scope) error { // *dstPtr is pointing to a element in the dictionary. We are not allowed // to modify it. Make a clone of it and decode into the clone. - val := (*dstPtr).Clone(d.allocators) + val := (*dstPtr).clone(d.allocators) if d.allocators.allocSizeChecker.IsOverLimit() { return pkg.ErrRecordAllocLimitExceeded } diff --git a/go/otel/otelstef/severitynumber.go b/go/otel/otelstef/severitynumber.go new file mode 100644 index 00000000..49b08b2a --- /dev/null +++ b/go/otel/otelstef/severitynumber.go @@ -0,0 +1,32 @@ +// Code generated by stefc. DO NOT EDIT. +package otelstef + +type SeverityNumber uint64 + +const ( + SeverityNumberUnspecified SeverityNumber = 0 + SeverityNumberTrace SeverityNumber = 1 + SeverityNumberTrace2 SeverityNumber = 2 + SeverityNumberTrace3 SeverityNumber = 3 + SeverityNumberTrace4 SeverityNumber = 4 + SeverityNumberDebug SeverityNumber = 5 + SeverityNumberDebug2 SeverityNumber = 6 + SeverityNumberDebug3 SeverityNumber = 7 + SeverityNumberDebug4 SeverityNumber = 8 + SeverityNumberInfo SeverityNumber = 9 + SeverityNumberInfo2 SeverityNumber = 10 + SeverityNumberInfo3 SeverityNumber = 11 + SeverityNumberInfo4 SeverityNumber = 12 + SeverityNumberWarn SeverityNumber = 13 + SeverityNumberWarn2 SeverityNumber = 14 + SeverityNumberWarn3 SeverityNumber = 15 + SeverityNumberWarn4 SeverityNumber = 16 + SeverityNumberError SeverityNumber = 17 + SeverityNumberError2 SeverityNumber = 18 + SeverityNumberError3 SeverityNumber = 19 + SeverityNumberError4 SeverityNumber = 20 + SeverityNumberFatal SeverityNumber = 21 + SeverityNumberFatal2 SeverityNumber = 22 + SeverityNumberFatal3 SeverityNumber = 23 + SeverityNumberFatal4 SeverityNumber = 24 +) diff --git a/go/otel/otelstef/span.go b/go/otel/otelstef/span.go index 8049820d..6b692035 100644 --- a/go/otel/otelstef/span.go +++ b/go/otel/otelstef/span.go @@ -128,6 +128,16 @@ func (s *Span) fixParent(parentModifiedFields *modifiedFields) { s.status.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Span) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.attributes.attachParent(&s.modifiedFields, fieldModifiedSpanAttributes) + s.events.attachParent(&s.modifiedFields, fieldModifiedSpanEvents) + s.links.attachParent(&s.modifiedFields, fieldModifiedSpanLinks) + s.status.attachParent(&s.modifiedFields, fieldModifiedSpanStatus) +} + func (s *Span) freeze() { if s.isFrozen() { return @@ -485,6 +495,16 @@ func (s *Span) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Span) clearModifiedRecursively() { + s.attributes.clearModifiedRecursively() + s.events.clearModifiedRecursively() + s.links.clearModifiedRecursively() + s.status.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Span) computeDiff(val *Span) (ret bool) { @@ -566,14 +586,10 @@ func (s *Span) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Span) cloneShared(allocators *Allocators) Span { - return s.Clone(allocators) -} - -func (s *Span) Clone(allocators *Allocators) Span { - c := Span{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Span) CloneTo(dst *Span, allocators *Allocators) { + *dst = Span{ traceID: s.traceID, spanID: s.spanID, traceState: s.traceState, @@ -585,11 +601,12 @@ func (s *Span) Clone(allocators *Allocators) Span { endTimeUnixNano: s.endTimeUnixNano, droppedAttributesCount: s.droppedAttributesCount, } - copyToNewAttributes(&c.attributes, &s.attributes, allocators) - copyToNewEventArray(&c.events, &s.events, allocators) - copyToNewLinkArray(&c.links, &s.links, allocators) - copyToNewSpanStatus(&c.status, &s.status, allocators) - return c + copyToNewAttributes(&dst.attributes, &s.attributes, allocators) + copyToNewEventArray(&dst.events, &s.events, allocators) + copyToNewLinkArray(&dst.links, &s.links, allocators) + copyToNewSpanStatus(&dst.status, &s.status, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/spans.go b/go/otel/otelstef/spans.go index d64d4422..17505aa1 100644 --- a/go/otel/otelstef/spans.go +++ b/go/otel/otelstef/spans.go @@ -104,6 +104,20 @@ func (s *Spans) fixParent(parentModifiedFields *modifiedFields) { s.span.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *Spans) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.envelope.attachParent(&s.modifiedFields, fieldModifiedSpansEnvelope) + if !s.resource.canBeShared() { + s.resource.attachParent(&s.modifiedFields, fieldModifiedSpansResource) + } + if !s.scope.canBeShared() { + s.scope.attachParent(&s.modifiedFields, fieldModifiedSpansScope) + } + s.span.attachParent(&s.modifiedFields, fieldModifiedSpansSpan) +} + func (s *Spans) freeze() { if s.isFrozen() { return @@ -151,7 +165,7 @@ func (s *Spans) SetResource(v *Resource) { } } else { if s.resource.canBeShared() { - s.resource = s.resource.Clone(&Allocators{}) + s.resource = s.resource.clone(&Allocators{}) } s.resource.CopyFrom(v) s.modifiedFields.markModified(fieldModifiedSpansResource) @@ -186,7 +200,7 @@ func (s *Spans) SetScope(v *Scope) { } } else { if s.scope.canBeShared() { - s.scope = s.scope.Clone(&Allocators{}) + s.scope = s.scope.clone(&Allocators{}) } s.scope.CopyFrom(v) s.modifiedFields.markModified(fieldModifiedSpansScope) @@ -249,6 +263,20 @@ func (s *Spans) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *Spans) clearModifiedRecursively() { + s.envelope.clearModifiedRecursively() + if !s.resource.canBeShared() { + s.resource.clearModifiedRecursively() + } + if !s.scope.canBeShared() { + s.scope.clearModifiedRecursively() + } + s.span.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *Spans) computeDiff(val *Spans) (ret bool) { @@ -280,20 +308,16 @@ func (s *Spans) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *Spans) cloneShared(allocators *Allocators) Spans { - return s.Clone(allocators) -} - -func (s *Spans) Clone(allocators *Allocators) Spans { - c := Spans{ - resource: s.resource.cloneShared(allocators), - scope: s.scope.cloneShared(allocators), - } - copyToNewEnvelope(&c.envelope, &s.envelope, allocators) - copyToNewSpan(&c.span, &s.span, allocators) - return c +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *Spans) CloneTo(dst *Spans, allocators *Allocators) { + *dst = Spans{} + copyToNewEnvelope(&dst.envelope, &s.envelope, allocators) + dst.resource = s.resource.cloneShared(allocators) + dst.scope = s.scope.cloneShared(allocators) + copyToNewSpan(&dst.span, &s.span, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -401,12 +425,12 @@ func (s *Spans) mutateRandom(random *rand.Rand, schem *schema.Schema, limiter *m s.resource = new(Resource) s.resource.init(&s.modifiedFields, fieldModifiedSpansResource) } else { - s.resource = s.resource.Clone(&Allocators{}) + s.resource = s.resource.clone(&Allocators{}) } } if s.resource.canBeShared() { // resource may be shared by pointer. Clone it to have exclusive ownership. - s.resource = s.resource.Clone(&Allocators{}) + s.resource = s.resource.clone(&Allocators{}) } s.resource.mutateRandom(random, schem, limiter) @@ -425,12 +449,12 @@ func (s *Spans) mutateRandom(random *rand.Rand, schem *schema.Schema, limiter *m s.scope = new(Scope) s.scope.init(&s.modifiedFields, fieldModifiedSpansScope) } else { - s.scope = s.scope.Clone(&Allocators{}) + s.scope = s.scope.clone(&Allocators{}) } } if s.scope.canBeShared() { // scope may be shared by pointer. Clone it to have exclusive ownership. - s.scope = s.scope.Clone(&Allocators{}) + s.scope = s.scope.clone(&Allocators{}) } s.scope.mutateRandom(random, schem, limiter) diff --git a/go/otel/otelstef/spanstatus.go b/go/otel/otelstef/spanstatus.go index 83f84e59..1baed495 100644 --- a/go/otel/otelstef/spanstatus.go +++ b/go/otel/otelstef/spanstatus.go @@ -80,6 +80,12 @@ func (s *SpanStatus) fixParent(parentModifiedFields *modifiedFields) { s.modifiedFields.parent = parentModifiedFields } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *SpanStatus) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit +} + func (s *SpanStatus) freeze() { s.modifiedFields.freeze() } @@ -146,6 +152,12 @@ func (s *SpanStatus) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *SpanStatus) clearModifiedRecursively() { + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *SpanStatus) computeDiff(val *SpanStatus) (ret bool) { @@ -167,18 +179,15 @@ func (s *SpanStatus) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *SpanStatus) cloneShared(allocators *Allocators) SpanStatus { - return s.Clone(allocators) -} - -func (s *SpanStatus) Clone(allocators *Allocators) SpanStatus { - c := SpanStatus{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *SpanStatus) CloneTo(dst *SpanStatus, allocators *Allocators) { + *dst = SpanStatus{ message: s.message, code: s.code, } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/summaryvalue.go b/go/otel/otelstef/summaryvalue.go index ed023a62..3ad18594 100644 --- a/go/otel/otelstef/summaryvalue.go +++ b/go/otel/otelstef/summaryvalue.go @@ -86,6 +86,13 @@ func (s *SummaryValue) fixParent(parentModifiedFields *modifiedFields) { s.quantileValues.fixParent(&s.modifiedFields) } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *SummaryValue) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + s.quantileValues.attachParent(&s.modifiedFields, fieldModifiedSummaryValueQuantileValues) +} + func (s *SummaryValue) freeze() { if s.isFrozen() { return @@ -177,6 +184,13 @@ func (s *SummaryValue) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *SummaryValue) clearModifiedRecursively() { + s.quantileValues.clearModifiedRecursively() + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *SummaryValue) computeDiff(val *SummaryValue) (ret bool) { @@ -203,19 +217,16 @@ func (s *SummaryValue) canBeShared() bool { return false } -// cloneShared returns a clone of s. It may return s if it is safe to share without cloning -// (for example if s is frozen). -func (s *SummaryValue) cloneShared(allocators *Allocators) SummaryValue { - return s.Clone(allocators) -} - -func (s *SummaryValue) Clone(allocators *Allocators) SummaryValue { - c := SummaryValue{ +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *SummaryValue) CloneTo(dst *SummaryValue, allocators *Allocators) { + *dst = SummaryValue{ count: s.count, sum: s.sum, } - copyToNewQuantileValueArray(&c.quantileValues, &s.quantileValues, allocators) - return c + copyToNewQuantileValueArray(&dst.quantileValues, &s.quantileValues, allocators) + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate diff --git a/go/otel/otelstef/uint64array.go b/go/otel/otelstef/uint64array.go index a719b69c..1e05df38 100644 --- a/go/otel/otelstef/uint64array.go +++ b/go/otel/otelstef/uint64array.go @@ -51,6 +51,12 @@ func (e *Uint64Array) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *Uint64Array) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit +} + func (e *Uint64Array) canBeShared() bool { // An array can never be shared. return false @@ -97,6 +103,11 @@ func (e *Uint64Array) setUnmodifiedRecursively() { } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e *Uint64Array) clearModifiedRecursively() { + +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *Uint64Array) computeDiff(val *Uint64Array) (ret bool) { diff --git a/go/otel/otelstef/writerstate.go b/go/otel/otelstef/writerstate.go index 91397071..d3808afe 100644 --- a/go/otel/otelstef/writerstate.go +++ b/go/otel/otelstef/writerstate.go @@ -16,6 +16,8 @@ type WriterState struct { // Dictionaries AnyValueString codecs.StringDictEncoderDict AttributeKey codecs.StringDictEncoderDict + LogEventName codecs.StringDictEncoderDict + LogSeverityText codecs.StringDictEncoderDict Metric MetricEncoderDict MetricDescription codecs.StringDictEncoderDict MetricName codecs.StringDictEncoderDict @@ -46,6 +48,8 @@ type WriterState struct { KeyValueListEncoder *KeyValueListEncoder LinkEncoder *LinkEncoder LinkArrayEncoder *LinkArrayEncoder + LogRecordEncoder *LogRecordEncoder + LogsEncoder *LogsEncoder MetricEncoder *MetricEncoder MetricsEncoder *MetricsEncoder PointEncoder *PointEncoder @@ -71,6 +75,8 @@ func (d *WriterState) Init(opts *pkg.WriterOptions) { // Init dictionaries d.AnyValueString.Init(&d.limiter) d.AttributeKey.Init(&d.limiter) + d.LogEventName.Init(&d.limiter) + d.LogSeverityText.Init(&d.limiter) d.Metric.Init(&d.limiter) d.MetricDescription.Init(&d.limiter) d.MetricName.Init(&d.limiter) @@ -89,6 +95,8 @@ func (d *WriterState) ResetDicts() { d.limiter.ResetDict() d.AnyValueString.Reset() d.AttributeKey.Reset() + d.LogEventName.Reset() + d.LogSeverityText.Reset() d.Metric.Reset() d.MetricDescription.Reset() d.MetricName.Reset() diff --git a/go/pdata/internal/otlptools/compare.go b/go/pdata/internal/otlptools/compare.go index 1318fe40..a4969bfd 100644 --- a/go/pdata/internal/otlptools/compare.go +++ b/go/pdata/internal/otlptools/compare.go @@ -1,9 +1,13 @@ package otlptools import ( + "bytes" + "cmp" + "slices" "strings" "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/pdata/ptrace" ) @@ -21,6 +25,9 @@ func Map2attrs(attributes pcommon.Map) []AttrAccessible { return true }, ) + slices.SortFunc(attrs, func(a, b AttrAccessible) int { + return strings.Compare(a.Key, b.Key) + }) return attrs } @@ -29,6 +36,10 @@ func CmpResourceMetrics(left, right pmetric.ResourceMetrics) int { if c != 0 { return c } + c = cmp.Compare(left.Resource().DroppedAttributesCount(), right.Resource().DroppedAttributesCount()) + if c != 0 { + return c + } return CmpAttrs(left.Resource().Attributes(), right.Resource().Attributes()) } @@ -37,6 +48,22 @@ func CmpResourceSpans(left, right ptrace.ResourceSpans) int { if c != 0 { return c } + c = cmp.Compare(left.Resource().DroppedAttributesCount(), right.Resource().DroppedAttributesCount()) + if c != 0 { + return c + } + return CmpAttrs(left.Resource().Attributes(), right.Resource().Attributes()) +} + +func CmpResourceLogs(left, right plog.ResourceLogs) int { + c := strings.Compare(left.SchemaUrl(), right.SchemaUrl()) + if c != 0 { + return c + } + c = cmp.Compare(left.Resource().DroppedAttributesCount(), right.Resource().DroppedAttributesCount()) + if c != 0 { + return c + } return CmpAttrs(left.Resource().Attributes(), right.Resource().Attributes()) } @@ -53,6 +80,10 @@ func CmpScopeMetrics(left, right pmetric.ScopeMetrics) int { if c != 0 { return c } + c = cmp.Compare(left.Scope().DroppedAttributesCount(), right.Scope().DroppedAttributesCount()) + if c != 0 { + return c + } return CmpAttrs(left.Scope().Attributes(), right.Scope().Attributes()) } @@ -70,10 +101,73 @@ func CmpScopeSpans(left, right ptrace.ScopeSpans) int { if c != 0 { return c } + c = cmp.Compare(left.Scope().DroppedAttributesCount(), right.Scope().DroppedAttributesCount()) + if c != 0 { + return c + } return CmpAttrs(left.Scope().Attributes(), right.Scope().Attributes()) } +func CmpScopeLogs(left, right plog.ScopeLogs) int { + c := strings.Compare(left.Scope().Name(), right.Scope().Name()) + if c != 0 { + return c + } + c = strings.Compare(left.Scope().Version(), right.Scope().Version()) + if c != 0 { + return c + } + c = strings.Compare(left.SchemaUrl(), right.SchemaUrl()) + if c != 0 { + return c + } + c = cmp.Compare(left.Scope().DroppedAttributesCount(), right.Scope().DroppedAttributesCount()) + if c != 0 { + return c + } + return CmpAttrs(left.Scope().Attributes(), right.Scope().Attributes()) +} + +// CmpLogRecords compares log records by every field encoded by the STEF logs +// converter. It provides a deterministic order for otherwise identical +// resource and scope groupings. +func CmpLogRecords(left, right plog.LogRecord) int { + if c := cmp.Compare(left.Timestamp(), right.Timestamp()); c != 0 { + return c + } + if c := cmp.Compare(left.ObservedTimestamp(), right.ObservedTimestamp()); c != 0 { + return c + } + leftTraceID, rightTraceID := left.TraceID(), right.TraceID() + if c := bytes.Compare(leftTraceID[:], rightTraceID[:]); c != 0 { + return c + } + leftSpanID, rightSpanID := left.SpanID(), right.SpanID() + if c := bytes.Compare(leftSpanID[:], rightSpanID[:]); c != 0 { + return c + } + if c := cmp.Compare(left.SeverityNumber(), right.SeverityNumber()); c != 0 { + return c + } + if c := strings.Compare(left.SeverityText(), right.SeverityText()); c != 0 { + return c + } + if c := CmpVal(left.Body(), right.Body()); c != 0 { + return c + } + if c := CmpAttrs(left.Attributes(), right.Attributes()); c != 0 { + return c + } + if c := cmp.Compare(left.DroppedAttributesCount(), right.DroppedAttributesCount()); c != 0 { + return c + } + if c := cmp.Compare(left.Flags(), right.Flags()); c != 0 { + return c + } + return strings.Compare(left.EventName(), right.EventName()) +} + func CmpAttrs(a, b pcommon.Map) int { left := Map2attrs(a) right := Map2attrs(b) @@ -114,6 +208,12 @@ func CmpVal(left, right pcommon.Value) int { case pcommon.ValueTypeBool: return CmpBool(left.Bool(), right.Bool()) + case pcommon.ValueTypeDouble: + return cmp.Compare(left.Double(), right.Double()) + + case pcommon.ValueTypeBytes: + return bytes.Compare(left.Bytes().AsRaw(), right.Bytes().AsRaw()) + case pcommon.ValueTypeSlice: left := left.Slice() right := right.Slice() @@ -131,8 +231,11 @@ func CmpVal(left, right pcommon.Value) int { case pcommon.ValueTypeEmpty: return 0 + case pcommon.ValueTypeMap: + return CmpAttrs(left.Map(), right.Map()) + default: - panic("comparison not implemented") + panic("unknown value type") } } diff --git a/go/pdata/internal/otlptools/otlpval2tef.go b/go/pdata/internal/otlptools/otlpval2tef.go index 07f4a34f..2e6d8e65 100644 --- a/go/pdata/internal/otlptools/otlpval2tef.go +++ b/go/pdata/internal/otlptools/otlpval2tef.go @@ -35,6 +35,11 @@ type Otlp2Stef struct { attrElems []elem } +// Value converts an OTLP AnyValue to its STEF representation. +func (o *Otlp2Stef) Value(src pcommon.Value, dst *otelstef.AnyValue) { + otlpValueToTefAnyValue(src, dst) +} + func (o *Otlp2Stef) ResourceSorted(dst *otelstef.Resource, src pcommon.Resource, schemaUrl string) { dst.SetSchemaURL(schemaUrl) o.MapSorted(src.Attributes(), dst.Attributes()) @@ -139,6 +144,7 @@ func otlpValueToTefAnyValue(val pcommon.Value, into *otelstef.AnyValue) { func(k string, v pcommon.Value) bool { kvList.SetKey(i, k) otlpValueToTefAnyValue(v, kvList.Value(i)) + i++ return true }, ) diff --git a/go/pdata/internal/otlptools/tef2otlpval.go b/go/pdata/internal/otlptools/tef2otlpval.go index 3f6d94de..24e64989 100644 --- a/go/pdata/internal/otlptools/tef2otlpval.go +++ b/go/pdata/internal/otlptools/tef2otlpval.go @@ -30,7 +30,7 @@ func TefToOtlpMap(in *otelstef.Attributes, out pcommon.Map) error { //decoder := anyvalue.Decoder{} for i := 0; i < in.Len(); i++ { val := out.PutEmpty(in.Key(i)) - err := tefAnyValueToOtlp(in.Value(i), val) + err := TefAnyValueToOtlp(in.Value(i), val) if err != nil { return err } @@ -40,7 +40,7 @@ func TefToOtlpMap(in *otelstef.Attributes, out pcommon.Map) error { var errDecode = errors.New("decode error") -func tefAnyValueToOtlp(anyVal *otelstef.AnyValue, into pcommon.Value) error { +func TefAnyValueToOtlp(anyVal *otelstef.AnyValue, into pcommon.Value) error { switch anyVal.Type() { case otelstef.AnyValueTypeString: into.SetStr(anyVal.String()) @@ -65,7 +65,7 @@ func tefAnyValueToOtlp(anyVal *otelstef.AnyValue, into pcommon.Value) error { arr := anyVal.Array() for i := 0; i < arr.Len(); i++ { val := values.AppendEmpty() - err := tefAnyValueToOtlp(arr.At(i), val) + err := TefAnyValueToOtlp(arr.At(i), val) if err != nil { return err } @@ -76,7 +76,7 @@ func tefAnyValueToOtlp(anyVal *otelstef.AnyValue, into pcommon.Value) error { kvList := anyVal.KVList() for i := 0; i < kvList.Len(); i++ { val := values.PutEmpty(kvList.Key(i)) - err := tefAnyValueToOtlp(kvList.Value(i), val) + err := TefAnyValueToOtlp(kvList.Value(i), val) if err != nil { return err } diff --git a/go/pdata/logs/otlp2stef.go b/go/pdata/logs/otlp2stef.go new file mode 100644 index 00000000..8e9946db --- /dev/null +++ b/go/pdata/logs/otlp2stef.go @@ -0,0 +1,20 @@ +package logs + +import ( + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/splunk/stef/go/otel/otelstef" +) + +// OtlpToStef defines a converter from OTLP logs to STEF format. +type OtlpToStef interface { + // Convert writes OTLP logs to the provided STEF writer. + // It does not call Flush on the writer. + Convert(src plog.Logs, writer *otelstef.LogsWriter) error +} + +// NewOtlpToStef returns an OTLP logs converter. When sorted is true, the +// converter groups equal resources and scopes and sorts log records by time. +func NewOtlpToStef(sorted bool) OtlpToStef { + return &OtlpToStefUnsorted{Sorted: sorted} +} diff --git a/go/pdata/logs/otlp2stef_unsorted.go b/go/pdata/logs/otlp2stef_unsorted.go new file mode 100644 index 00000000..a40b55a8 --- /dev/null +++ b/go/pdata/logs/otlp2stef_unsorted.go @@ -0,0 +1,123 @@ +package logs + +import ( + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/splunk/stef/go/otel/otelstef" + "github.com/splunk/stef/go/pdata/internal/otlptools" + "github.com/splunk/stef/go/pkg" +) + +// OtlpToStefUnsorted converts OTLP logs to STEF. If Sorted is true, it first +// groups equal resources and scopes and orders log records by timestamp. +type OtlpToStefUnsorted struct { + otlp2stef otlptools.Otlp2Stef + Sorted bool +} + +var _ OtlpToStef = (*OtlpToStefUnsorted)(nil) + +func (c *OtlpToStefUnsorted) Convert(src plog.Logs, writer *otelstef.LogsWriter) error { + otlp2stef := &c.otlp2stef + resourceLogs := src.ResourceLogs() + + if c.Sorted { + resourceLogs.Sort(func(a, b plog.ResourceLogs) bool { + return otlptools.CmpResourceLogs(a, b) < 0 + }) + for i := 0; i < resourceLogs.Len()-1; { + if otlptools.CmpResourceLogs(resourceLogs.At(i), resourceLogs.At(i+1)) != 0 { + i++ + continue + } + resourceLogs.At(i + 1).ScopeLogs().MoveAndAppendTo(resourceLogs.At(i).ScopeLogs()) + removeResourceLogsAt(resourceLogs, i+1) + } + } + + for i := 0; i < resourceLogs.Len(); i++ { + rl := resourceLogs.At(i) + if c.Sorted { + otlp2stef.ResourceSorted(writer.Record.Resource(), rl.Resource(), rl.SchemaUrl()) + } else { + otlp2stef.ResourceUnsorted(writer.Record.Resource(), rl.Resource(), rl.SchemaUrl()) + } + scopeLogs := rl.ScopeLogs() + + if c.Sorted { + scopeLogs.Sort(func(a, b plog.ScopeLogs) bool { + return otlptools.CmpScopeLogs(a, b) < 0 + }) + for j := 0; j < scopeLogs.Len()-1; { + if otlptools.CmpScopeLogs(scopeLogs.At(j), scopeLogs.At(j+1)) != 0 { + j++ + continue + } + scopeLogs.At(j + 1).LogRecords().MoveAndAppendTo(scopeLogs.At(j).LogRecords()) + removeScopeLogsAt(scopeLogs, j+1) + } + } + + for j := 0; j < scopeLogs.Len(); j++ { + sl := scopeLogs.At(j) + if c.Sorted { + otlp2stef.ScopeSorted(writer.Record.Scope(), sl.Scope(), sl.SchemaUrl()) + sortLogRecords(sl.LogRecords()) + } else { + otlp2stef.ScopeUnsorted(writer.Record.Scope(), sl.Scope(), sl.SchemaUrl()) + } + + for k := 0; k < sl.LogRecords().Len(); k++ { + logRecordToStef(sl.LogRecords().At(k), writer.Record.Log(), otlp2stef, c.Sorted) + if err := writer.Write(); err != nil { + return err + } + } + } + } + return nil +} + +func removeResourceLogsAt(logs plog.ResourceLogsSlice, index int) { + i := 0 + logs.RemoveIf(func(plog.ResourceLogs) bool { + remove := i == index + i++ + return remove + }) +} + +func removeScopeLogsAt(logs plog.ScopeLogsSlice, index int) { + i := 0 + logs.RemoveIf(func(plog.ScopeLogs) bool { + remove := i == index + i++ + return remove + }) +} + +func sortLogRecords(logs plog.LogRecordSlice) { + logs.Sort(func(a, b plog.LogRecord) bool { + return otlptools.CmpLogRecords(a, b) < 0 + }) +} + +func logRecordToStef(src plog.LogRecord, dst *otelstef.LogRecord, converter *otlptools.Otlp2Stef, sorted bool) { + dst.SetTimeUnixNano(uint64(src.Timestamp())) + dst.SetObservedTimeUnixNano(uint64(src.ObservedTimestamp())) + dst.SetSeverityNumber(otelstef.SeverityNumber(src.SeverityNumber())) + dst.SetSeverityText(src.SeverityText()) + converter.Value(src.Body(), dst.Body()) + if sorted { + converter.MapSorted(src.Attributes(), dst.Attributes()) + } else { + converter.MapUnsorted(src.Attributes(), dst.Attributes()) + } + dst.SetDroppedAttributesCount(uint64(src.DroppedAttributesCount())) + dst.SetFlags(uint64(src.Flags())) + traceID := src.TraceID() + spanID := src.SpanID() + dst.SetTraceID(pkg.Bytes(traceID[:])) + dst.SetSpanID(pkg.Bytes(spanID[:])) + dst.SetEventName(src.EventName()) +} diff --git a/go/pdata/logs/otlp2stef_unsorted_test.go b/go/pdata/logs/otlp2stef_unsorted_test.go new file mode 100644 index 00000000..017bfc3b --- /dev/null +++ b/go/pdata/logs/otlp2stef_unsorted_test.go @@ -0,0 +1,106 @@ +package logs + +import ( + "bytes" + "io" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/splunk/stef/go/otel/otelstef" + "github.com/splunk/stef/go/pkg" +) + +func TestConvertLogRecord(t *testing.T) { + src := plog.NewLogs() + rl := src.ResourceLogs().AppendEmpty() + rl.SetSchemaUrl("resource-schema") + rl.Resource().Attributes().PutStr("service.name", "checkout") + rl.Resource().SetDroppedAttributesCount(1) + + sl := rl.ScopeLogs().AppendEmpty() + sl.SetSchemaUrl("scope-schema") + sl.Scope().SetName("logger") + sl.Scope().SetVersion("1.2.3") + sl.Scope().Attributes().PutBool("scope-attribute", true) + sl.Scope().SetDroppedAttributesCount(2) + + logRecord := sl.LogRecords().AppendEmpty() + logRecord.SetTimestamp(pcommon.Timestamp(100)) + logRecord.SetObservedTimestamp(pcommon.Timestamp(200)) + logRecord.SetSeverityNumber(plog.SeverityNumberError) + logRecord.SetSeverityText("ERROR") + body := logRecord.Body().SetEmptyMap() + body.PutStr("message", "failed") + body.PutInt("attempt", 3) + logRecord.Attributes().PutStr("log.attribute", "value") + logRecord.SetDroppedAttributesCount(3) + logRecord.SetFlags(plog.DefaultLogRecordFlags.WithIsSampled(true)) + traceID := pcommon.TraceID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + spanID := pcommon.SpanID{1, 2, 3, 4, 5, 6, 7, 8} + logRecord.SetTraceID(traceID) + logRecord.SetSpanID(spanID) + logRecord.SetEventName("exception") + + buf := &pkg.MemChunkWriter{} + writer, err := otelstef.NewLogsWriter(buf, pkg.WriterOptions{}) + require.NoError(t, err) + require.NoError(t, (&OtlpToStefUnsorted{}).Convert(src, writer)) + require.NoError(t, writer.Flush()) + require.EqualValues(t, 1, writer.RecordCount()) + + reader, err := otelstef.NewLogsReader(bytes.NewReader(buf.Bytes())) + require.NoError(t, err) + require.NoError(t, reader.Read(pkg.ReadOptions{})) + + require.Equal(t, "resource-schema", reader.Record.Resource().SchemaURL()) + require.EqualValues(t, 1, reader.Record.Resource().DroppedAttributesCount()) + require.Equal(t, "logger", reader.Record.Scope().Name()) + require.Equal(t, "1.2.3", reader.Record.Scope().Version()) + require.Equal(t, "scope-schema", reader.Record.Scope().SchemaURL()) + require.EqualValues(t, 2, reader.Record.Scope().DroppedAttributesCount()) + + got := reader.Record.Log() + require.EqualValues(t, 100, got.TimeUnixNano()) + require.EqualValues(t, 200, got.ObservedTimeUnixNano()) + require.Equal(t, otelstef.SeverityNumberError, got.SeverityNumber()) + require.Equal(t, "ERROR", got.SeverityText()) + require.Equal(t, otelstef.AnyValueTypeKVList, got.Body().Type()) + require.Equal(t, 2, got.Body().KVList().Len()) + require.Equal(t, 1, got.Attributes().Len()) + require.EqualValues(t, 3, got.DroppedAttributesCount()) + require.EqualValues(t, logRecord.Flags(), got.Flags()) + require.Equal(t, pkg.Bytes(traceID[:]), got.TraceID()) + require.Equal(t, pkg.Bytes(spanID[:]), got.SpanID()) + require.Equal(t, "exception", got.EventName()) + require.ErrorIs(t, reader.Read(pkg.ReadOptions{}), io.EOF) +} + +func TestConvertSortedGroupsAndOrdersLogs(t *testing.T) { + src := plog.NewLogs() + for _, timestamp := range []pcommon.Timestamp{200, 100} { + rl := src.ResourceLogs().AppendEmpty() + rl.Resource().Attributes().PutStr("service.name", "checkout") + sl := rl.ScopeLogs().AppendEmpty() + sl.Scope().SetName("logger") + logRecord := sl.LogRecords().AppendEmpty() + logRecord.SetTimestamp(timestamp) + } + + buf := &pkg.MemChunkWriter{} + writer, err := otelstef.NewLogsWriter(buf, pkg.WriterOptions{}) + require.NoError(t, err) + require.NoError(t, NewOtlpToStef(true).Convert(src, writer)) + require.NoError(t, writer.Flush()) + require.EqualValues(t, 2, writer.RecordCount()) + + reader, err := otelstef.NewLogsReader(bytes.NewReader(buf.Bytes())) + require.NoError(t, err) + for _, timestamp := range []uint64{100, 200} { + require.NoError(t, reader.Read(pkg.ReadOptions{})) + require.Equal(t, timestamp, reader.Record.Log().TimeUnixNano()) + } + require.ErrorIs(t, reader.Read(pkg.ReadOptions{}), io.EOF) +} diff --git a/go/pdata/logs/sortedbyresource/converter.go b/go/pdata/logs/sortedbyresource/converter.go new file mode 100644 index 00000000..bde80f44 --- /dev/null +++ b/go/pdata/logs/sortedbyresource/converter.go @@ -0,0 +1,58 @@ +package sortedbyresource + +import ( + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/splunk/stef/go/otel/otelstef" + "github.com/splunk/stef/go/pdata/internal/otlptools" + "github.com/splunk/stef/go/pkg" +) + +func OtlpToSortedTree(data plog.Logs) *SortedTree { + tree := NewSortedLogs() + converter := otlptools.Otlp2Stef{} + resourceLogs := data.ResourceLogs() + + for i := 0; i < resourceLogs.Len(); i++ { + srcResource := resourceLogs.At(i) + resource := otelstef.NewResource() + converter.ResourceSorted(resource, srcResource.Resource(), srcResource.SchemaUrl()) + resource.Freeze() + byResource := tree.ByResource(resource) + + scopeLogs := srcResource.ScopeLogs() + for j := 0; j < scopeLogs.Len(); j++ { + srcScope := scopeLogs.At(j) + scope := otelstef.NewScope() + converter.ScopeSorted(scope, srcScope.Scope(), srcScope.SchemaUrl()) + scope.Freeze() + byScope := byResource.ByScope(scope) + + logRecords := srcScope.LogRecords() + for k := 0; k < logRecords.Len(); k++ { + log := otelstef.NewLogRecord() + logRecordToStef(logRecords.At(k), log, &converter) + byScope.Append(log) + } + } + } + + tree.SortValues() + return tree +} + +func logRecordToStef(src plog.LogRecord, dst *otelstef.LogRecord, converter *otlptools.Otlp2Stef) { + dst.SetTimeUnixNano(uint64(src.Timestamp())) + dst.SetObservedTimeUnixNano(uint64(src.ObservedTimestamp())) + dst.SetSeverityNumber(otelstef.SeverityNumber(src.SeverityNumber())) + dst.SetSeverityText(src.SeverityText()) + converter.Value(src.Body(), dst.Body()) + converter.MapSorted(src.Attributes(), dst.Attributes()) + dst.SetDroppedAttributesCount(uint64(src.DroppedAttributesCount())) + dst.SetFlags(uint64(src.Flags())) + traceID := src.TraceID() + spanID := src.SpanID() + dst.SetTraceID(pkg.Bytes(traceID[:])) + dst.SetSpanID(pkg.Bytes(spanID[:])) + dst.SetEventName(src.EventName()) +} diff --git a/go/pdata/logs/sortedbyresource/converter_test.go b/go/pdata/logs/sortedbyresource/converter_test.go new file mode 100644 index 00000000..bb51e8f0 --- /dev/null +++ b/go/pdata/logs/sortedbyresource/converter_test.go @@ -0,0 +1,44 @@ +package sortedbyresource + +import ( + "bytes" + "io" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/splunk/stef/go/otel/otelstef" + "github.com/splunk/stef/go/pkg" +) + +func TestOtlpToSortedTreeGroupsAndSortsLogs(t *testing.T) { + src := plog.NewLogs() + for _, timestamp := range []pcommon.Timestamp{200, 100} { + resourceLogs := src.ResourceLogs().AppendEmpty() + resourceLogs.Resource().Attributes().PutStr("service.name", "checkout") + scopeLogs := resourceLogs.ScopeLogs().AppendEmpty() + scopeLogs.Scope().SetName("logger") + scopeLogs.LogRecords().AppendEmpty().SetTimestamp(timestamp) + } + + tree := OtlpToSortedTree(src) + output := &pkg.MemChunkWriter{} + writer, err := otelstef.NewLogsWriter(output, pkg.WriterOptions{}) + require.NoError(t, err) + require.NoError(t, tree.ToStef(writer)) + require.NoError(t, writer.Flush()) + + reader, err := otelstef.NewLogsReader(bytes.NewReader(output.Bytes())) + require.NoError(t, err) + for i, timestamp := range []uint64{100, 200} { + require.NoError(t, reader.Read(pkg.ReadOptions{})) + require.Equal(t, timestamp, reader.Record.Log().TimeUnixNano()) + if i > 0 { + require.False(t, reader.Record.IsResourceModified()) + require.False(t, reader.Record.IsScopeModified()) + } + } + require.ErrorIs(t, reader.Read(pkg.ReadOptions{}), io.EOF) +} diff --git a/go/pdata/logs/sortedbyresource/sortedlogs.go b/go/pdata/logs/sortedbyresource/sortedlogs.go new file mode 100644 index 00000000..991a7448 --- /dev/null +++ b/go/pdata/logs/sortedbyresource/sortedlogs.go @@ -0,0 +1,117 @@ +package sortedbyresource + +import ( + "io" + "slices" + + "modernc.org/b/v2" + + "github.com/splunk/stef/go/otel/otelstef" +) + +type SortedTree struct { + byResource *b.Tree[*otelstef.Resource, *ByResource] +} + +type ByResource struct { + byScope *b.Tree[*otelstef.Scope, *ByScope] +} + +type ByScope struct { + logs LogRecords +} + +type LogRecords []*otelstef.LogRecord + +func NewSortedLogs() *SortedTree { + return &SortedTree{ + byResource: b.TreeNew[*otelstef.Resource, *ByResource](otelstef.CmpResource), + } +} + +func (s *SortedTree) ByResource(resource *otelstef.Resource) *ByResource { + byResource, exists := s.byResource.Get(resource) + if !exists { + byResource = &ByResource{ + byScope: b.TreeNew[*otelstef.Scope, *ByScope](otelstef.CmpScope), + } + s.byResource.Set(resource, byResource) + } + return byResource +} + +func (r *ByResource) ByScope(scope *otelstef.Scope) *ByScope { + byScope, exists := r.byScope.Get(scope) + if !exists { + byScope = &ByScope{} + r.byScope.Set(scope, byScope) + } + return byScope +} + +func (s *ByScope) Append(log *otelstef.LogRecord) { + s.logs = append(s.logs, log) +} + +func (s *SortedTree) SortValues() { + _ = s.Iter(func(_ *otelstef.Resource, resource *ByResource) error { + return resource.Iter(func(_ *otelstef.Scope, scope *ByScope) error { + slices.SortFunc(scope.logs, otelstef.CmpLogRecord) + return nil + }) + }) +} + +func (s *SortedTree) ToStef(writer *otelstef.LogsWriter) error { + return s.Iter(func(resource *otelstef.Resource, byResource *ByResource) error { + writer.Record.SetResource(resource) + return byResource.Iter(func(scope *otelstef.Scope, byScope *ByScope) error { + writer.Record.SetScope(scope) + for _, log := range byScope.logs { + writer.Record.Log().CopyFrom(log) + if err := writer.Write(); err != nil { + return err + } + } + return nil + }) + }) +} + +func (s *SortedTree) Iter(f func(*otelstef.Resource, *ByResource) error) error { + iter, err := s.byResource.SeekFirst() + if err != nil { + return nil + } + for { + key, value, err := iter.Next() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + if err := f(key, value); err != nil { + return err + } + } +} + +func (r *ByResource) Iter(f func(*otelstef.Scope, *ByScope) error) error { + iter, err := r.byScope.SeekFirst() + if err != nil { + return nil + } + for { + key, value, err := iter.Next() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + if err := f(key, value); err != nil { + return err + } + } +} diff --git a/go/pdata/logs/stef2otlp.go b/go/pdata/logs/stef2otlp.go new file mode 100644 index 00000000..f71467cb --- /dev/null +++ b/go/pdata/logs/stef2otlp.go @@ -0,0 +1,14 @@ +package logs + +import ( + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/splunk/stef/go/otel/otelstef" +) + +// StefToOtlp defines a converter from STEF format to OTLP logs. +type StefToOtlp interface { + // Convert reads STEF records and returns the corresponding OTLP logs. + // When untilEOF is false, it stops at the end of the current frame. + Convert(reader *otelstef.LogsReader, untilEOF bool) (plog.Logs, error) +} diff --git a/go/pdata/logs/stef2otlp_unsorted.go b/go/pdata/logs/stef2otlp_unsorted.go new file mode 100644 index 00000000..4d351826 --- /dev/null +++ b/go/pdata/logs/stef2otlp_unsorted.go @@ -0,0 +1,117 @@ +package logs + +import ( + "errors" + "io" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/splunk/stef/go/otel/otelstef" + "github.com/splunk/stef/go/pdata/internal/otlptools" + "github.com/splunk/stef/go/pkg" +) + +// StefToOtlpUnsorted reads STEF records and converts them to OTLP logs. +type StefToOtlpUnsorted struct{} + +var _ StefToOtlp = (*StefToOtlpUnsorted)(nil) + +// Convert reads until EOF when untilEOF is true. Otherwise, it reads at least +// one record and stops at the end of the current frame. +func (c *StefToOtlpUnsorted) Convert(reader *otelstef.LogsReader, untilEOF bool) (plog.Logs, error) { + logs := plog.NewLogs() + + if err := reader.Read(pkg.ReadOptions{}); err != nil { + return logs, err + } + + var resourceLogs plog.ResourceLogs + var scopeLogs plog.ScopeLogs + record := &reader.Record + modified := true + + for { + if modified || record.IsResourceModified() { + modified = true + resourceLogs = logs.ResourceLogs().AppendEmpty() + if err := resourceToOtlp(record.Resource(), resourceLogs); err != nil { + return logs, err + } + } + + if modified || record.IsScopeModified() { + modified = true + scopeLogs = resourceLogs.ScopeLogs().AppendEmpty() + if err := scopeToOtlp(record.Scope(), scopeLogs); err != nil { + return logs, err + } + } + + if err := logRecordToOtlp(record.Log(), scopeLogs.LogRecords().AppendEmpty()); err != nil { + return logs, err + } + modified = false + + var err error + if untilEOF { + err = reader.Read(pkg.ReadOptions{}) + if errors.Is(err, io.EOF) { + break + } + } else { + err = reader.Read(pkg.ReadOptions{TillEndOfFrame: true}) + if errors.Is(err, pkg.ErrEndOfFrame) { + break + } + } + if err != nil { + return logs, err + } + } + + return logs, nil +} + +func resourceToOtlp(src *otelstef.Resource, dst plog.ResourceLogs) error { + if src == nil { + return nil + } + dst.SetSchemaUrl(src.SchemaURL()) + dst.Resource().SetDroppedAttributesCount(uint32(src.DroppedAttributesCount())) + return otlptools.TefToOtlpMap(src.Attributes(), dst.Resource().Attributes()) +} + +func scopeToOtlp(src *otelstef.Scope, dst plog.ScopeLogs) error { + if src == nil { + return nil + } + dst.SetSchemaUrl(src.SchemaURL()) + dst.Scope().SetName(src.Name()) + dst.Scope().SetVersion(src.Version()) + dst.Scope().SetDroppedAttributesCount(uint32(src.DroppedAttributesCount())) + return otlptools.TefToOtlpMap(src.Attributes(), dst.Scope().Attributes()) +} + +func logRecordToOtlp(src *otelstef.LogRecord, dst plog.LogRecord) error { + dst.SetTimestamp(pcommon.Timestamp(src.TimeUnixNano())) + dst.SetObservedTimestamp(pcommon.Timestamp(src.ObservedTimeUnixNano())) + dst.SetSeverityNumber(plog.SeverityNumber(src.SeverityNumber())) + dst.SetSeverityText(src.SeverityText()) + if err := otlptools.TefAnyValueToOtlp(src.Body(), dst.Body()); err != nil { + return err + } + if err := otlptools.TefToOtlpMap(src.Attributes(), dst.Attributes()); err != nil { + return err + } + dst.SetDroppedAttributesCount(uint32(src.DroppedAttributesCount())) + dst.SetFlags(plog.LogRecordFlags(src.Flags())) + var traceID pcommon.TraceID + copy(traceID[:], src.TraceID()) + dst.SetTraceID(traceID) + var spanID pcommon.SpanID + copy(spanID[:], src.SpanID()) + dst.SetSpanID(spanID) + dst.SetEventName(src.EventName()) + return nil +} diff --git a/go/pdata/logs/testtools/normalize.go b/go/pdata/logs/testtools/normalize.go new file mode 100644 index 00000000..5753f38e --- /dev/null +++ b/go/pdata/logs/testtools/normalize.go @@ -0,0 +1,112 @@ +package testtools + +import ( + "slices" + "strings" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/splunk/stef/go/pdata/internal/otlptools" +) + +// NormalizeLogs canonicalizes grouping, record order, and map order so that +// semantically equivalent logs have identical protobuf encodings. +func NormalizeLogs(data plog.Logs) { + resourceLogs := data.ResourceLogs() + for i := 0; i < resourceLogs.Len(); i++ { + normalizeMap(resourceLogs.At(i).Resource().Attributes()) + } + resourceLogs.Sort(func(a, b plog.ResourceLogs) bool { + return otlptools.CmpResourceLogs(a, b) < 0 + }) + for i := 0; i < resourceLogs.Len()-1; { + if otlptools.CmpResourceLogs(resourceLogs.At(i), resourceLogs.At(i+1)) != 0 { + i++ + continue + } + resourceLogs.At(i + 1).ScopeLogs().MoveAndAppendTo(resourceLogs.At(i).ScopeLogs()) + removeResourceLogsAt(resourceLogs, i+1) + } + for i := 0; i < resourceLogs.Len(); i++ { + normalizeScopeLogs(resourceLogs.At(i).ScopeLogs()) + } +} + +func normalizeScopeLogs(scopeLogs plog.ScopeLogsSlice) { + for i := 0; i < scopeLogs.Len(); i++ { + normalizeMap(scopeLogs.At(i).Scope().Attributes()) + } + scopeLogs.Sort(func(a, b plog.ScopeLogs) bool { + return otlptools.CmpScopeLogs(a, b) < 0 + }) + for i := 0; i < scopeLogs.Len()-1; { + if otlptools.CmpScopeLogs(scopeLogs.At(i), scopeLogs.At(i+1)) != 0 { + i++ + continue + } + scopeLogs.At(i + 1).LogRecords().MoveAndAppendTo(scopeLogs.At(i).LogRecords()) + removeScopeLogsAt(scopeLogs, i+1) + } + for i := 0; i < scopeLogs.Len(); i++ { + records := scopeLogs.At(i).LogRecords() + for j := 0; j < records.Len(); j++ { + normalizeValue(records.At(j).Body()) + normalizeMap(records.At(j).Attributes()) + } + records.Sort(func(a, b plog.LogRecord) bool { + return otlptools.CmpLogRecords(a, b) < 0 + }) + } +} + +func normalizeMap(m pcommon.Map) { + type entry struct { + key string + value pcommon.Value + } + entries := make([]entry, 0, m.Len()) + m.Range(func(key string, value pcommon.Value) bool { + normalizeValue(value) + valueCopy := pcommon.NewValueEmpty() + value.CopyTo(valueCopy) + entries = append(entries, entry{key: key, value: valueCopy}) + return true + }) + slices.SortFunc(entries, func(a, b entry) int { return strings.Compare(a.key, b.key) }) + m.Clear() + m.EnsureCapacity(len(entries)) + for _, entry := range entries { + entry.value.CopyTo(m.PutEmpty(entry.key)) + } +} + +func normalizeValue(value pcommon.Value) { + switch value.Type() { + case pcommon.ValueTypeMap: + normalizeMap(value.Map()) + case pcommon.ValueTypeSlice: + values := value.Slice() + for i := 0; i < values.Len(); i++ { + normalizeValue(values.At(i)) + } + } +} + +func removeResourceLogsAt(logs plog.ResourceLogsSlice, index int) { + i := 0 + logs.RemoveIf(func(plog.ResourceLogs) bool { + remove := i == index + i++ + return remove + }) +} + +func removeScopeLogsAt(logs plog.ScopeLogsSlice, index int) { + i := 0 + logs.RemoveIf(func(plog.ScopeLogs) bool { + remove := i == index + i++ + return remove + }) +} diff --git a/go/pdata/logs/testtools/normalize_test.go b/go/pdata/logs/testtools/normalize_test.go new file mode 100644 index 00000000..2ed3f0b0 --- /dev/null +++ b/go/pdata/logs/testtools/normalize_test.go @@ -0,0 +1,64 @@ +package testtools + +import ( + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/plog" +) + +func TestNormalizeLogsCanonicalizesLogRecordOrder(t *testing.T) { + tests := map[string]func(plog.LogRecord){ + "severity number": func(record plog.LogRecord) { + record.SetSeverityNumber(plog.SeverityNumberError) + }, + "severity text": func(record plog.LogRecord) { + record.SetSeverityText("error") + }, + "body": func(record plog.LogRecord) { + record.Body().SetStr("body") + }, + "attributes": func(record plog.LogRecord) { + record.Attributes().PutStr("key", "value") + }, + "dropped attribute count": func(record plog.LogRecord) { + record.SetDroppedAttributesCount(1) + }, + "flags": func(record plog.LogRecord) { + record.SetFlags(plog.DefaultLogRecordFlags.WithIsSampled(true)) + }, + "event name": func(record plog.LogRecord) { + record.SetEventName("event") + }, + } + + for name, mutate := range tests { + t.Run(name, func(t *testing.T) { + forward := newLogsWithRecordOrder(false, mutate) + reversed := newLogsWithRecordOrder(true, mutate) + + NormalizeLogs(forward) + NormalizeLogs(reversed) + + marshaler := plog.ProtoMarshaler{} + forwardBytes, err := marshaler.MarshalLogs(forward) + require.NoError(t, err) + reversedBytes, err := marshaler.MarshalLogs(reversed) + require.NoError(t, err) + require.Equal(t, forwardBytes, reversedBytes) + }) + } +} + +func newLogsWithRecordOrder(reversed bool, mutate func(plog.LogRecord)) plog.Logs { + logs := plog.NewLogs() + records := logs.ResourceLogs().AppendEmpty().ScopeLogs().AppendEmpty().LogRecords() + if reversed { + mutate(records.AppendEmpty()) + records.AppendEmpty() + } else { + records.AppendEmpty() + mutate(records.AppendEmpty()) + } + return logs +} diff --git a/go/pdata/metrics/pdatasort.go b/go/pdata/metrics/pdatasort.go index 444127d0..ded42302 100644 --- a/go/pdata/metrics/pdatasort.go +++ b/go/pdata/metrics/pdatasort.go @@ -1,6 +1,7 @@ package metrics import ( + "cmp" "strings" "go.opentelemetry.io/collector/pdata/pcommon" @@ -199,6 +200,10 @@ func (s *PDataSorter) cmpResourceMetrics(left, right pmetric.ResourceMetrics) in if c != 0 { return c } + c = cmp.Compare(left.Resource().DroppedAttributesCount(), right.Resource().DroppedAttributesCount()) + if c != 0 { + return c + } return s.cmpAttrs(left.Resource().Attributes(), right.Resource().Attributes()) } @@ -215,6 +220,10 @@ func (s *PDataSorter) cmpScopeMetrics(left, right pmetric.ScopeMetrics) int { if c != 0 { return c } + c = cmp.Compare(left.Scope().DroppedAttributesCount(), right.Scope().DroppedAttributesCount()) + if c != 0 { + return c + } return s.cmpAttrs(left.Scope().Attributes(), right.Scope().Attributes()) } diff --git a/go/pdata/metrics/sortedbyresource/sortedresource.go b/go/pdata/metrics/sortedbyresource/sortedresource.go index b13a4ecb..814d9e11 100644 --- a/go/pdata/metrics/sortedbyresource/sortedresource.go +++ b/go/pdata/metrics/sortedbyresource/sortedresource.go @@ -91,7 +91,9 @@ func (s *SortedTree) ByResource(resource *otelstef.Resource) *ByResource { elem, exists := s.byResource.Get(resource) if !exists { elem = &ByResource{byScope: b.TreeNew[*otelstef.Scope, *ByScope](otelstef.CmpScope), allocators: &s.allocators} - s.byResource.Set(resource.Clone(&s.allocators), elem) + clone := s.allocators.Resource.Alloc() + resource.CloneTo(clone, &s.allocators) + s.byResource.Set(clone, elem) } return elem } @@ -100,7 +102,9 @@ func (m *ByResource) ByScope(scope *otelstef.Scope) *ByScope { elem, exists := m.byScope.Get(scope) if !exists { elem = &ByScope{byMetrics: b.TreeNew[*otelstef.Metric, *ByMetric](otelstef.CmpMetric), allocators: m.allocators} - m.byScope.Set(scope.Clone(m.allocators), elem) + clone := m.allocators.Scope.Alloc() + scope.CloneTo(clone, m.allocators) + m.byScope.Set(clone, elem) } return elem } @@ -114,7 +118,9 @@ func (m *ByScope) ByMetric( byAttrs: b.TreeNew[*otelstef.Attributes, *Points](otelstef.CmpAttributes), allocators: m.allocators, } - m.byMetrics.Set(metric.Clone(m.allocators), elem) + clone := m.allocators.Metric.Alloc() + metric.CloneTo(clone, m.allocators) + m.byMetrics.Set(clone, elem) } return elem } diff --git a/go/pkg/types.go b/go/pkg/types.go index 9aa5d4e0..51369bc6 100644 --- a/go/pkg/types.go +++ b/go/pkg/types.go @@ -1,6 +1,7 @@ package pkg import ( + "cmp" "math/rand/v2" "strconv" "strings" @@ -42,13 +43,7 @@ func BoolCompare(left, right bool) int { } func Float64Compare(left, right float64) int { - if left > right { - return 1 - } - if left < right { - return -1 - } - return 0 + return cmp.Compare(left, right) } func StringCompare(left, right string) int { diff --git a/java/src/main/java/com/example/otelstef/AnyValue.java b/java/src/main/java/com/example/otelstef/AnyValue.java index 458617f0..4eaeed5b 100644 --- a/java/src/main/java/com/example/otelstef/AnyValue.java +++ b/java/src/main/java/com/example/otelstef/AnyValue.java @@ -219,14 +219,8 @@ public void setBytes(BytesValue v) { // Clone returns a deep copy of this oneof. public AnyValue clone() { AnyValue cpy = new AnyValue(); - cpy.typ = this.typ; - cpy.string = this.string; - cpy.bool = this.bool; - cpy.int64 = this.int64; - cpy.float64 = this.float64; - cpy.array = this.array.clone(); - cpy.kVList = this.kVList.clone(); - cpy.bytes = this.bytes; + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } @@ -493,15 +487,23 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash( - string, - bool, - int64, - float64, - array, - kVList, - bytes, - typ - ); + switch (this.typ) { + case TypeString: + return Objects.hash(typ, string); + case TypeBool: + return Objects.hash(typ, bool); + case TypeInt64: + return Objects.hash(typ, int64); + case TypeFloat64: + return Objects.hash(typ, float64); + case TypeArray: + return Objects.hash(typ, array); + case TypeKVList: + return Objects.hash(typ, kVList); + case TypeBytes: + return Objects.hash(typ, bytes); + default: + return Objects.hash(typ); + } } } diff --git a/java/src/main/java/com/example/otelstef/AnyValueArrayEncoder.java b/java/src/main/java/com/example/otelstef/AnyValueArrayEncoder.java index 774e8431..3a0530e6 100644 --- a/java/src/main/java/com/example/otelstef/AnyValueArrayEncoder.java +++ b/java/src/main/java/com/example/otelstef/AnyValueArrayEncoder.java @@ -17,6 +17,8 @@ class AnyValueArrayEncoder { private AnyValueEncoder elemEncoder; private WriterState state; private boolean isRecursive = false; + // Set after reset so every element in the first non-empty array is encoded in full. + private boolean forceElements; public void init(WriterState state, WriteColumnSet columns) throws IOException { this.state = state; @@ -43,20 +45,28 @@ public void init(WriterState state, WriteColumnSet columns) throws IOException { } public void reset() { + forceElements = true; if (!isRecursive) { elemEncoder.reset(); } } public void encode(AnyValueArray arr) throws IOException { + encode(arr, false); + } + + public void encode(AnyValueArray arr, boolean forceAllElements) throws IOException { int oldBitLen = buf.bitCount(); // Write the length of the array. int newLen = arr.elemsLen; buf.writeUvarintCompact(newLen); - + boolean forceElementsNow = forceAllElements || forceElements; for (int i = 0; i < newLen; i++) { - elemEncoder.encode(arr.elems[i]); + elemEncoder.encode(arr.elems[i], forceElementsNow); + } + if (newLen != 0) { + forceElements = false; } // Account written bits in the limiter. @@ -71,4 +81,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } } - diff --git a/java/src/main/java/com/example/otelstef/AnyValueEncoder.java b/java/src/main/java/com/example/otelstef/AnyValueEncoder.java index 429a5f77..ea9f670f 100644 --- a/java/src/main/java/com/example/otelstef/AnyValueEncoder.java +++ b/java/src/main/java/com/example/otelstef/AnyValueEncoder.java @@ -149,6 +149,12 @@ public void reset() { // Encode encodes val into buf public void encode(AnyValue val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf and propagates forced full encoding through + // the selected non-primitive value. + public void encode(AnyValue val, boolean forceAllFields) throws IOException { AnyValue.Type typ = val.typ; if (typ.getValue() > fieldCount) { // The current field type is not supported in target schema. Encode the type as None. @@ -181,11 +187,11 @@ public void encode(AnyValue val) throws IOException { break; case TypeArray: // Encode Array - arrayEncoder.encode(val.array); + arrayEncoder.encode(val.array, forceAllFields); break; case TypeKVList: // Encode KVList - kVListEncoder.encode(val.kVList); + kVListEncoder.encode(val.kVList, forceAllFields); break; case TypeBytes: // Encode Bytes diff --git a/java/src/main/java/com/example/otelstef/Attributes.java b/java/src/main/java/com/example/otelstef/Attributes.java index ae53eb57..fc18559f 100644 --- a/java/src/main/java/com/example/otelstef/Attributes.java +++ b/java/src/main/java/com/example/otelstef/Attributes.java @@ -123,6 +123,7 @@ void markUnmodifiedRecursively() { for (int i=0; i fieldCount) { // The current field type is not supported in target schema. Encode the type as None. diff --git a/java/src/main/java/com/example/otelstef/ExpHistogramBuckets.java b/java/src/main/java/com/example/otelstef/ExpHistogramBuckets.java index 46462d85..c2116ee4 100644 --- a/java/src/main/java/com/example/otelstef/ExpHistogramBuckets.java +++ b/java/src/main/java/com/example/otelstef/ExpHistogramBuckets.java @@ -111,8 +111,8 @@ void markUnmodifiedRecursively() { public ExpHistogramBuckets clone() { ExpHistogramBuckets cpy = new ExpHistogramBuckets(); - cpy.offset = this.offset; - cpy.bucketCounts = this.bucketCounts.clone(); + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/ExpHistogramBucketsEncoder.java b/java/src/main/java/com/example/otelstef/ExpHistogramBucketsEncoder.java index 746dd822..2f35d206 100644 --- a/java/src/main/java/com/example/otelstef/ExpHistogramBucketsEncoder.java +++ b/java/src/main/java/com/example/otelstef/ExpHistogramBucketsEncoder.java @@ -87,18 +87,26 @@ public void reset() { // encode encodes val into buf public void encode(ExpHistogramBuckets val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(ExpHistogramBuckets val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = ExpHistogramBuckets.fieldModifiedOffset | ExpHistogramBuckets.fieldModifiedBucketCounts | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -116,7 +124,7 @@ public void encode(ExpHistogramBuckets val) throws IOException { if ((fieldMask & ExpHistogramBuckets.fieldModifiedBucketCounts) != 0) { // Encode BucketCounts - this.bucketCountsEncoder.encode(val.bucketCounts); + this.bucketCountsEncoder.encode(val.bucketCounts, forceFields); } // Account written bits in the limiter. @@ -152,4 +160,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/ExpHistogramValue.java b/java/src/main/java/com/example/otelstef/ExpHistogramValue.java index 9e80e51b..7647e1db 100644 --- a/java/src/main/java/com/example/otelstef/ExpHistogramValue.java +++ b/java/src/main/java/com/example/otelstef/ExpHistogramValue.java @@ -361,15 +361,8 @@ void markUnmodifiedRecursively() { public ExpHistogramValue clone() { ExpHistogramValue cpy = new ExpHistogramValue(); - cpy.count = this.count; - cpy.sum = this.sum; - cpy.min = this.min; - cpy.max = this.max; - cpy.scale = this.scale; - cpy.zeroCount = this.zeroCount; - cpy.positiveBuckets = this.positiveBuckets.clone(); - cpy.negativeBuckets = this.negativeBuckets.clone(); - cpy.zeroThreshold = this.zeroThreshold; + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/ExpHistogramValueEncoder.java b/java/src/main/java/com/example/otelstef/ExpHistogramValueEncoder.java index 912423ed..c2699a89 100644 --- a/java/src/main/java/com/example/otelstef/ExpHistogramValueEncoder.java +++ b/java/src/main/java/com/example/otelstef/ExpHistogramValueEncoder.java @@ -182,15 +182,22 @@ public void reset() { // encode encodes val into buf public void encode(ExpHistogramValue val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(ExpHistogramValue val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = ExpHistogramValue.fieldModifiedCount | ExpHistogramValue.fieldModifiedSum | @@ -201,6 +208,7 @@ public void encode(ExpHistogramValue val) throws IOException { ExpHistogramValue.fieldModifiedPositiveBuckets | ExpHistogramValue.fieldModifiedNegativeBuckets | ExpHistogramValue.fieldModifiedZeroThreshold | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -245,12 +253,12 @@ public void encode(ExpHistogramValue val) throws IOException { if ((fieldMask & ExpHistogramValue.fieldModifiedPositiveBuckets) != 0) { // Encode PositiveBuckets - this.positiveBucketsEncoder.encode(val.positiveBuckets); + this.positiveBucketsEncoder.encode(val.positiveBuckets, forceFields); } if ((fieldMask & ExpHistogramValue.fieldModifiedNegativeBuckets) != 0) { // Encode NegativeBuckets - this.negativeBucketsEncoder.encode(val.negativeBuckets); + this.negativeBucketsEncoder.encode(val.negativeBuckets, forceFields); } if ((fieldMask & ExpHistogramValue.fieldModifiedZeroThreshold) != 0) { @@ -348,4 +356,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/Float64ArrayEncoder.java b/java/src/main/java/com/example/otelstef/Float64ArrayEncoder.java index 230b361b..ec25d21f 100644 --- a/java/src/main/java/com/example/otelstef/Float64ArrayEncoder.java +++ b/java/src/main/java/com/example/otelstef/Float64ArrayEncoder.java @@ -33,12 +33,15 @@ public void reset() { } public void encode(Float64Array arr) throws IOException { + encode(arr, false); + } + + public void encode(Float64Array arr, boolean forceAllElements) throws IOException { int oldBitLen = buf.bitCount(); // Write the length of the array. int newLen = arr.elemsLen; buf.writeUvarintCompact(newLen); - for (int i = 0; i < newLen; i++) { elemEncoder.encode(arr.elems[i]); } @@ -55,4 +58,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } } - diff --git a/java/src/main/java/com/example/otelstef/HistogramValue.java b/java/src/main/java/com/example/otelstef/HistogramValue.java index 10eb6daa..51fd8894 100644 --- a/java/src/main/java/com/example/otelstef/HistogramValue.java +++ b/java/src/main/java/com/example/otelstef/HistogramValue.java @@ -247,11 +247,8 @@ void markUnmodifiedRecursively() { public HistogramValue clone() { HistogramValue cpy = new HistogramValue(); - cpy.count = this.count; - cpy.sum = this.sum; - cpy.min = this.min; - cpy.max = this.max; - cpy.bucketCounts = this.bucketCounts.clone(); + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/HistogramValueEncoder.java b/java/src/main/java/com/example/otelstef/HistogramValueEncoder.java index 6e3a5bb0..ff2663de 100644 --- a/java/src/main/java/com/example/otelstef/HistogramValueEncoder.java +++ b/java/src/main/java/com/example/otelstef/HistogramValueEncoder.java @@ -124,21 +124,29 @@ public void reset() { // encode encodes val into buf public void encode(HistogramValue val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(HistogramValue val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = HistogramValue.fieldModifiedCount | HistogramValue.fieldModifiedSum | HistogramValue.fieldModifiedMin | HistogramValue.fieldModifiedMax | HistogramValue.fieldModifiedBucketCounts | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -173,7 +181,7 @@ public void encode(HistogramValue val) throws IOException { if ((fieldMask & HistogramValue.fieldModifiedBucketCounts) != 0) { // Encode BucketCounts - this.bucketCountsEncoder.encode(val.bucketCounts); + this.bucketCountsEncoder.encode(val.bucketCounts, forceFields); } // Account written bits in the limiter. @@ -233,4 +241,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/KeyValueList.java b/java/src/main/java/com/example/otelstef/KeyValueList.java index 44eb6a0d..4783c9a4 100644 --- a/java/src/main/java/com/example/otelstef/KeyValueList.java +++ b/java/src/main/java/com/example/otelstef/KeyValueList.java @@ -123,6 +123,7 @@ void markUnmodifiedRecursively() { for (int i=0; i right. + public static int compare(LogRecord left, LogRecord right) { + if (left == null) { + if (right == null) { + return 0; + } + return -1; + } + if (right == null) { + return 1; + } + int c; + + // Compare TimeUnixNano field. + c = Types.Uint64Compare(left.timeUnixNano, right.timeUnixNano); + if (c != 0) { + return c; + } + + // Compare ObservedTimeUnixNano field. + c = Types.Uint64Compare(left.observedTimeUnixNano, right.observedTimeUnixNano); + if (c != 0) { + return c; + } + + // Compare SeverityNumber field. + c = Types.Uint64Compare(left.severityNumber, right.severityNumber); + if (c != 0) { + return c; + } + + // Compare SeverityText field. + c = Types.StringCompare(left.severityText, right.severityText); + if (c != 0) { + return c; + } + + // Compare Body field. + c = AnyValue.compare(left.body, right.body); + if (c != 0) { + return c; + } + + // Compare Attributes field. + c = Attributes.compare(left.attributes, right.attributes); + if (c != 0) { + return c; + } + + // Compare DroppedAttributesCount field. + c = Types.Uint64Compare(left.droppedAttributesCount, right.droppedAttributesCount); + if (c != 0) { + return c; + } + + // Compare Flags field. + c = Types.Uint64Compare(left.flags, right.flags); + if (c != 0) { + return c; + } + + // Compare TraceID field. + c = Types.BytesCompare(left.traceID, right.traceID); + if (c != 0) { + return c; + } + + // Compare SpanID field. + c = Types.BytesCompare(left.spanID, right.spanID); + if (c != 0) { + return c; + } + + // Compare EventName field. + c = Types.StringCompare(left.eventName, right.eventName); + if (c != 0) { + return c; + } + + return 0; + } + + // mutateRandom mutates fields in a random, deterministic manner using random as a deterministic generator. + void mutateRandom(Random random, CommonMutateRandomLimiter limiter) { + final int fieldCount = Math.max(11,2); // At least 2 to ensure we don't recurse infinitely if there is only 1 field. + + if (random.nextInt(fieldCount) == 0) { + this.setTimeUnixNano(Types.Uint64Random(random)); + } + + if (random.nextInt(fieldCount) == 0) { + this.setObservedTimeUnixNano(Types.Uint64Random(random)); + } + + if (random.nextInt(fieldCount) == 0) { + this.setSeverityNumber((Types.Uint64Random(random) & 0x7FFFFFFFFFFFFFFFL) % 25); + } + + if (random.nextInt(fieldCount) == 0) { + this.setSeverityText(Types.StringRandom(random)); + } + + if (random.nextInt(fieldCount) == 0) { + this.body.mutateRandom(random, limiter); + } + + if (random.nextInt(fieldCount) == 0) { + this.attributes.mutateRandom(random, limiter); + } + + if (random.nextInt(fieldCount) == 0) { + this.setDroppedAttributesCount(Types.Uint64Random(random)); + } + + if (random.nextInt(fieldCount) == 0) { + this.setFlags(Types.Uint64Random(random)); + } + + if (random.nextInt(fieldCount) == 0) { + this.setTraceID(Types.BytesRandom(random)); + } + + if (random.nextInt(fieldCount) == 0) { + this.setSpanID(Types.BytesRandom(random)); + } + + if (random.nextInt(fieldCount) == 0) { + this.setEventName(Types.StringRandom(random)); + } + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) { + return false; + } + return equals((LogRecord)o); + } + + @Override + public int hashCode() { + return Objects.hash( + timeUnixNano, + observedTimeUnixNano, + severityNumber, + severityText, + body, + attributes, + droppedAttributesCount, + flags, + traceID, + spanID, + eventName + ); + } + + +} diff --git a/java/src/main/java/com/example/otelstef/LogRecordDecoder.java b/java/src/main/java/com/example/otelstef/LogRecordDecoder.java new file mode 100644 index 00000000..4bce5854 --- /dev/null +++ b/java/src/main/java/com/example/otelstef/LogRecordDecoder.java @@ -0,0 +1,324 @@ +// Code generated by stefc. DO NOT EDIT. +// LogRecordDecoder implements decoding of LogRecord +package com.example.otelstef; + +import net.stef.BitsReader; +import net.stef.Helper; +import net.stef.ReadColumnSet; +import net.stef.ReadableColumn; +import net.stef.codecs.*; + +import java.io.IOException; + +class LogRecordDecoder { + private final BitsReader buf = new BitsReader(); + private ReadableColumn column; + private int fieldCount; + + + + private Uint64Decoder timeUnixNanoDecoder; + private boolean isTimeUnixNanoRecursive = false; // Indicates TimeUnixNano field's type is recursive. + private Uint64Decoder observedTimeUnixNanoDecoder; + private boolean isObservedTimeUnixNanoRecursive = false; // Indicates ObservedTimeUnixNano field's type is recursive. + private Uint64Decoder severityNumberDecoder; + private boolean isSeverityNumberRecursive = false; // Indicates SeverityNumber field's type is recursive. + private StringDictDecoder severityTextDecoder; + private boolean isSeverityTextRecursive = false; // Indicates SeverityText field's type is recursive. + private AnyValueDecoder bodyDecoder; + private boolean isBodyRecursive = false; // Indicates Body field's type is recursive. + private AttributesDecoder attributesDecoder; + private boolean isAttributesRecursive = false; // Indicates Attributes field's type is recursive. + private Uint64Decoder droppedAttributesCountDecoder; + private boolean isDroppedAttributesCountRecursive = false; // Indicates DroppedAttributesCount field's type is recursive. + private Uint64Decoder flagsDecoder; + private boolean isFlagsRecursive = false; // Indicates Flags field's type is recursive. + private BytesDecoder traceIDDecoder; + private boolean isTraceIDRecursive = false; // Indicates TraceID field's type is recursive. + private BytesDecoder spanIDDecoder; + private boolean isSpanIDRecursive = false; // Indicates SpanID field's type is recursive. + private StringDictDecoder eventNameDecoder; + private boolean isEventNameRecursive = false; // Indicates EventName field's type is recursive. + + + // Init is called once in the lifetime of the stream. + public void init(ReaderState state, ReadColumnSet columns) throws IOException { + // Remember this encoder in the state so that we can detect recursion. + if (state.LogRecordDecoder != null) { + throw new IllegalStateException("cannot initialize LogRecordDecoder: already initialized"); + } + state.LogRecordDecoder = this; + + try { + fieldCount = state.getStructFieldCounts().getLogRecordFieldCount(); + column = columns.getColumn(); + + if (this.fieldCount <= 0) { + return; // TimeUnixNano and subsequent fields are skipped. + } + timeUnixNanoDecoder = new Uint64Decoder(); + timeUnixNanoDecoder.init(columns.addSubColumn()); + if (this.fieldCount <= 1) { + return; // ObservedTimeUnixNano and subsequent fields are skipped. + } + observedTimeUnixNanoDecoder = new Uint64Decoder(); + observedTimeUnixNanoDecoder.init(columns.addSubColumn()); + if (this.fieldCount <= 2) { + return; // SeverityNumber and subsequent fields are skipped. + } + severityNumberDecoder = new Uint64Decoder(); + severityNumberDecoder.init(columns.addSubColumn()); + if (this.fieldCount <= 3) { + return; // SeverityText and subsequent fields are skipped. + } + severityTextDecoder = new StringDictDecoder(); + severityTextDecoder.init(state.LogSeverityText, columns.addSubColumn()); + if (this.fieldCount <= 4) { + return; // Body and subsequent fields are skipped. + } + if (state.AnyValueDecoder != null) { + // Recursion detected, use the existing decoder. + bodyDecoder = state.AnyValueDecoder; + isBodyRecursive = true; // Mark that we are using a recursive decoder. + } else { + bodyDecoder = new AnyValueDecoder(); + bodyDecoder.init(state, columns.addSubColumn()); + } + if (this.fieldCount <= 5) { + return; // Attributes and subsequent fields are skipped. + } + if (state.AttributesDecoder != null) { + // Recursion detected, use the existing decoder. + attributesDecoder = state.AttributesDecoder; + isAttributesRecursive = true; // Mark that we are using a recursive decoder. + } else { + attributesDecoder = new AttributesDecoder(); + attributesDecoder.init(state, columns.addSubColumn()); + } + if (this.fieldCount <= 6) { + return; // DroppedAttributesCount and subsequent fields are skipped. + } + droppedAttributesCountDecoder = new Uint64Decoder(); + droppedAttributesCountDecoder.init(columns.addSubColumn()); + if (this.fieldCount <= 7) { + return; // Flags and subsequent fields are skipped. + } + flagsDecoder = new Uint64Decoder(); + flagsDecoder.init(columns.addSubColumn()); + if (this.fieldCount <= 8) { + return; // TraceID and subsequent fields are skipped. + } + traceIDDecoder = new BytesDecoder(); + traceIDDecoder.init(columns.addSubColumn()); + if (this.fieldCount <= 9) { + return; // SpanID and subsequent fields are skipped. + } + spanIDDecoder = new BytesDecoder(); + spanIDDecoder.init(columns.addSubColumn()); + if (this.fieldCount <= 10) { + return; // EventName and subsequent fields are skipped. + } + eventNameDecoder = new StringDictDecoder(); + eventNameDecoder.init(state.LogEventName, columns.addSubColumn()); + } finally { + state.LogRecordDecoder = null; + } + } + + // continueDecoding is called at the start of the frame to continue decoding column data. + // This should set the decoder's source buffer, so the new decoding continues from + // the supplied column data. This should NOT reset the internal state of the decoder, + // since columns can cross frame boundaries and the new column data is considered + // continuation of that same column in the previous frame. + public void continueDecoding() { + this.buf.reset(this.column.getData()); + + if (this.fieldCount <= 0) { + return; // TimeUnixNano and subsequent fields are skipped. + } + timeUnixNanoDecoder.continueDecoding(); + if (this.fieldCount <= 1) { + return; // ObservedTimeUnixNano and subsequent fields are skipped. + } + observedTimeUnixNanoDecoder.continueDecoding(); + if (this.fieldCount <= 2) { + return; // SeverityNumber and subsequent fields are skipped. + } + severityNumberDecoder.continueDecoding(); + if (this.fieldCount <= 3) { + return; // SeverityText and subsequent fields are skipped. + } + severityTextDecoder.continueDecoding(); + if (this.fieldCount <= 4) { + return; // Body and subsequent fields are skipped. + } + + if (!isBodyRecursive) { + bodyDecoder.continueDecoding(); + } + + if (this.fieldCount <= 5) { + return; // Attributes and subsequent fields are skipped. + } + + if (!isAttributesRecursive) { + attributesDecoder.continueDecoding(); + } + + if (this.fieldCount <= 6) { + return; // DroppedAttributesCount and subsequent fields are skipped. + } + droppedAttributesCountDecoder.continueDecoding(); + if (this.fieldCount <= 7) { + return; // Flags and subsequent fields are skipped. + } + flagsDecoder.continueDecoding(); + if (this.fieldCount <= 8) { + return; // TraceID and subsequent fields are skipped. + } + traceIDDecoder.continueDecoding(); + if (this.fieldCount <= 9) { + return; // SpanID and subsequent fields are skipped. + } + spanIDDecoder.continueDecoding(); + if (this.fieldCount <= 10) { + return; // EventName and subsequent fields are skipped. + } + eventNameDecoder.continueDecoding(); + } + + public void reset() { + + if (fieldCount <= 0) { + // TimeUnixNano and all subsequent fields are skipped. + return; + } + timeUnixNanoDecoder.reset(); + if (fieldCount <= 1) { + // ObservedTimeUnixNano and all subsequent fields are skipped. + return; + } + observedTimeUnixNanoDecoder.reset(); + if (fieldCount <= 2) { + // SeverityNumber and all subsequent fields are skipped. + return; + } + severityNumberDecoder.reset(); + if (fieldCount <= 3) { + // SeverityText and all subsequent fields are skipped. + return; + } + severityTextDecoder.reset(); + if (fieldCount <= 4) { + // Body and all subsequent fields are skipped. + return; + } + if (!isBodyRecursive) { + bodyDecoder.reset(); + } + if (fieldCount <= 5) { + // Attributes and all subsequent fields are skipped. + return; + } + if (!isAttributesRecursive) { + attributesDecoder.reset(); + } + if (fieldCount <= 6) { + // DroppedAttributesCount and all subsequent fields are skipped. + return; + } + droppedAttributesCountDecoder.reset(); + if (fieldCount <= 7) { + // Flags and all subsequent fields are skipped. + return; + } + flagsDecoder.reset(); + if (fieldCount <= 8) { + // TraceID and all subsequent fields are skipped. + return; + } + traceIDDecoder.reset(); + if (fieldCount <= 9) { + // SpanID and all subsequent fields are skipped. + return; + } + spanIDDecoder.reset(); + if (fieldCount <= 10) { + // EventName and all subsequent fields are skipped. + return; + } + eventNameDecoder.reset(); + } + + public LogRecord decode(LogRecord dstPtr) throws IOException { + LogRecord val = dstPtr; + // Read bits that indicate which fields follow. + val.modifiedFields.mask = buf.readBits(fieldCount); + + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedTimeUnixNano) != 0) { + // Field is changed. + val.timeUnixNano = timeUnixNanoDecoder.decode(); + } + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedObservedTimeUnixNano) != 0) { + // Field is changed. + val.observedTimeUnixNano = observedTimeUnixNanoDecoder.decode(); + } + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedSeverityNumber) != 0) { + // Field is changed. + val.severityNumber = severityNumberDecoder.decode(); + } + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedSeverityText) != 0) { + // Field is changed. + val.severityText = severityTextDecoder.decode(); + } + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedBody) != 0) { + // Field is changed. + if (val.body == null) { + val.body = new AnyValue(val.modifiedFields, LogRecord.fieldModifiedBody); + } + val.body = bodyDecoder.decode(val.body); + } + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedAttributes) != 0) { + // Field is changed. + if (val.attributes == null) { + val.attributes = new Attributes(val.modifiedFields, LogRecord.fieldModifiedAttributes); + } + val.attributes = attributesDecoder.decode(val.attributes); + } + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedDroppedAttributesCount) != 0) { + // Field is changed. + val.droppedAttributesCount = droppedAttributesCountDecoder.decode(); + } + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedFlags) != 0) { + // Field is changed. + val.flags = flagsDecoder.decode(); + } + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedTraceID) != 0) { + // Field is changed. + val.traceID = traceIDDecoder.decode(); + } + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedSpanID) != 0) { + // Field is changed. + val.spanID = spanIDDecoder.decode(); + } + + if ((val.modifiedFields.mask & LogRecord.fieldModifiedEventName) != 0) { + // Field is changed. + val.eventName = eventNameDecoder.decode(); + } + + + return val; + } +} + diff --git a/java/src/main/java/com/example/otelstef/LogRecordEncoder.java b/java/src/main/java/com/example/otelstef/LogRecordEncoder.java new file mode 100644 index 00000000..1a9d13f1 --- /dev/null +++ b/java/src/main/java/com/example/otelstef/LogRecordEncoder.java @@ -0,0 +1,407 @@ +// Code generated by stefc. DO NOT EDIT. +// LogRecordEncoder implements encoding of LogRecord +package com.example.otelstef; + +import net.stef.BitsWriter; +import net.stef.Helper; +import net.stef.SizeLimiter; +import net.stef.WriteColumnSet; +import net.stef.codecs.*; + +import java.io.IOException; + +class LogRecordEncoder { + private BitsWriter buf = new BitsWriter(); + private SizeLimiter limiter; + + // forceModifiedFields is set to true if the next encoding operation + // must write all fields, whether they are modified or not. + // This is used after frame restarts so that the data can be decoded + // from the frame start. + private boolean forceModifiedFields; + + + private Uint64Encoder timeUnixNanoEncoder; + private boolean isTimeUnixNanoRecursive = false; // Indicates TimeUnixNano field's type is recursive. + private Uint64Encoder observedTimeUnixNanoEncoder; + private boolean isObservedTimeUnixNanoRecursive = false; // Indicates ObservedTimeUnixNano field's type is recursive. + private Uint64Encoder severityNumberEncoder; + private boolean isSeverityNumberRecursive = false; // Indicates SeverityNumber field's type is recursive. + private StringDictEncoder severityTextEncoder; + private boolean isSeverityTextRecursive = false; // Indicates SeverityText field's type is recursive. + private AnyValueEncoder bodyEncoder; + private boolean isBodyRecursive = false; // Indicates Body field's type is recursive. + private AttributesEncoder attributesEncoder; + private boolean isAttributesRecursive = false; // Indicates Attributes field's type is recursive. + private Uint64Encoder droppedAttributesCountEncoder; + private boolean isDroppedAttributesCountRecursive = false; // Indicates DroppedAttributesCount field's type is recursive. + private Uint64Encoder flagsEncoder; + private boolean isFlagsRecursive = false; // Indicates Flags field's type is recursive. + private BytesEncoder traceIDEncoder; + private boolean isTraceIDRecursive = false; // Indicates TraceID field's type is recursive. + private BytesEncoder spanIDEncoder; + private boolean isSpanIDRecursive = false; // Indicates SpanID field's type is recursive. + private StringDictEncoder eventNameEncoder; + private boolean isEventNameRecursive = false; // Indicates EventName field's type is recursive. + + + private long keepFieldMask; + private int fieldCount; + + + public void init(WriterState state, WriteColumnSet columns) throws IOException { + // Remember this encoder in the state so that we can detect recursion. + if (state.LogRecordEncoder != null) { + throw new IllegalStateException("cannot initialize LogRecordEncoder: already initialized"); + } + state.LogRecordEncoder = this; + + try { + limiter = state.getLimiter(); + + fieldCount = state.getStructFieldCounts().getLogRecordFieldCount(); + keepFieldMask = ~((~0L) << fieldCount); + // Init encoder for TimeUnixNano field. + if (fieldCount <= 0) { + return; // TimeUnixNano and subsequent fields are skipped. + } + timeUnixNanoEncoder = new Uint64Encoder(); + timeUnixNanoEncoder.init(limiter, columns.addSubColumn()); + // Init encoder for ObservedTimeUnixNano field. + if (fieldCount <= 1) { + return; // ObservedTimeUnixNano and subsequent fields are skipped. + } + observedTimeUnixNanoEncoder = new Uint64Encoder(); + observedTimeUnixNanoEncoder.init(limiter, columns.addSubColumn()); + // Init encoder for SeverityNumber field. + if (fieldCount <= 2) { + return; // SeverityNumber and subsequent fields are skipped. + } + severityNumberEncoder = new Uint64Encoder(); + severityNumberEncoder.init(limiter, columns.addSubColumn()); + // Init encoder for SeverityText field. + if (fieldCount <= 3) { + return; // SeverityText and subsequent fields are skipped. + } + severityTextEncoder = new StringDictEncoder(); + severityTextEncoder.init(state.LogSeverityText, limiter, columns.addSubColumn()); + // Init encoder for Body field. + if (fieldCount <= 4) { + return; // Body and subsequent fields are skipped. + } + if (state.AnyValueEncoder != null) { + // Recursion detected, use the existing encoder. + bodyEncoder = state.AnyValueEncoder; + isBodyRecursive = true; + } else { + bodyEncoder = new AnyValueEncoder(); + bodyEncoder.init(state, columns.addSubColumn()); + } + // Init encoder for Attributes field. + if (fieldCount <= 5) { + return; // Attributes and subsequent fields are skipped. + } + if (state.AttributesEncoder != null) { + // Recursion detected, use the existing encoder. + attributesEncoder = state.AttributesEncoder; + isAttributesRecursive = true; + } else { + attributesEncoder = new AttributesEncoder(); + attributesEncoder.init(state, columns.addSubColumn()); + } + // Init encoder for DroppedAttributesCount field. + if (fieldCount <= 6) { + return; // DroppedAttributesCount and subsequent fields are skipped. + } + droppedAttributesCountEncoder = new Uint64Encoder(); + droppedAttributesCountEncoder.init(limiter, columns.addSubColumn()); + // Init encoder for Flags field. + if (fieldCount <= 7) { + return; // Flags and subsequent fields are skipped. + } + flagsEncoder = new Uint64Encoder(); + flagsEncoder.init(limiter, columns.addSubColumn()); + // Init encoder for TraceID field. + if (fieldCount <= 8) { + return; // TraceID and subsequent fields are skipped. + } + traceIDEncoder = new BytesEncoder(); + traceIDEncoder.init(limiter, columns.addSubColumn()); + // Init encoder for SpanID field. + if (fieldCount <= 9) { + return; // SpanID and subsequent fields are skipped. + } + spanIDEncoder = new BytesEncoder(); + spanIDEncoder.init(limiter, columns.addSubColumn()); + // Init encoder for EventName field. + if (fieldCount <= 10) { + return; // EventName and subsequent fields are skipped. + } + eventNameEncoder = new StringDictEncoder(); + eventNameEncoder.init(state.LogEventName, limiter, columns.addSubColumn()); + } finally { + state.LogRecordEncoder = null; + } + } + + public void reset() { + // Since we are resetting the state of encoder make sure the next encode() + // call forcefully writes all fields and does not attempt to skip. + this.forceModifiedFields = true; + + if (fieldCount <= 0) { + return; // TimeUnixNano and all subsequent fields are skipped. + } + timeUnixNanoEncoder.reset(); + if (fieldCount <= 1) { + return; // ObservedTimeUnixNano and all subsequent fields are skipped. + } + observedTimeUnixNanoEncoder.reset(); + if (fieldCount <= 2) { + return; // SeverityNumber and all subsequent fields are skipped. + } + severityNumberEncoder.reset(); + if (fieldCount <= 3) { + return; // SeverityText and all subsequent fields are skipped. + } + severityTextEncoder.reset(); + if (fieldCount <= 4) { + return; // Body and all subsequent fields are skipped. + } + + if (!isBodyRecursive) { + bodyEncoder.reset(); + } + + if (fieldCount <= 5) { + return; // Attributes and all subsequent fields are skipped. + } + + if (!isAttributesRecursive) { + attributesEncoder.reset(); + } + + if (fieldCount <= 6) { + return; // DroppedAttributesCount and all subsequent fields are skipped. + } + droppedAttributesCountEncoder.reset(); + if (fieldCount <= 7) { + return; // Flags and all subsequent fields are skipped. + } + flagsEncoder.reset(); + if (fieldCount <= 8) { + return; // TraceID and all subsequent fields are skipped. + } + traceIDEncoder.reset(); + if (fieldCount <= 9) { + return; // SpanID and all subsequent fields are skipped. + } + spanIDEncoder.reset(); + if (fieldCount <= 10) { + return; // EventName and all subsequent fields are skipped. + } + eventNameEncoder.reset(); + } + + // encode encodes val into buf + public void encode(LogRecord val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(LogRecord val, boolean forceAllFields) throws IOException { + int oldLen = this.buf.bitCount(); + + + + // Mask that describes what fields are encoded. Start with all modified fields. + long fieldMask = val.modifiedFields.mask; + // On an encoder restart or when requested by a containing collection, + // force writing of all fields. + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { + fieldMask = + LogRecord.fieldModifiedTimeUnixNano | + LogRecord.fieldModifiedObservedTimeUnixNano | + LogRecord.fieldModifiedSeverityNumber | + LogRecord.fieldModifiedSeverityText | + LogRecord.fieldModifiedBody | + LogRecord.fieldModifiedAttributes | + LogRecord.fieldModifiedDroppedAttributesCount | + LogRecord.fieldModifiedFlags | + LogRecord.fieldModifiedTraceID | + LogRecord.fieldModifiedSpanID | + LogRecord.fieldModifiedEventName | 0L; + this.forceModifiedFields = false; + } + + // Only write fields that we want to write. See init() for keepFieldMask. + fieldMask &= this.keepFieldMask; + + // Write bits to indicate which fields follow. + this.buf.writeBits(fieldMask, this.fieldCount); + + // Encode modified, present fields. + + if ((fieldMask & LogRecord.fieldModifiedTimeUnixNano) != 0) { + // Encode TimeUnixNano + this.timeUnixNanoEncoder.encode(val.timeUnixNano); + } + + if ((fieldMask & LogRecord.fieldModifiedObservedTimeUnixNano) != 0) { + // Encode ObservedTimeUnixNano + this.observedTimeUnixNanoEncoder.encode(val.observedTimeUnixNano); + } + + if ((fieldMask & LogRecord.fieldModifiedSeverityNumber) != 0) { + // Encode SeverityNumber + this.severityNumberEncoder.encode(val.severityNumber); + } + + if ((fieldMask & LogRecord.fieldModifiedSeverityText) != 0) { + // Encode SeverityText + this.severityTextEncoder.encode(val.severityText); + } + + if ((fieldMask & LogRecord.fieldModifiedBody) != 0) { + // Encode Body + this.bodyEncoder.encode(val.body, forceFields); + } + + if ((fieldMask & LogRecord.fieldModifiedAttributes) != 0) { + // Encode Attributes + this.attributesEncoder.encode(val.attributes, forceFields); + } + + if ((fieldMask & LogRecord.fieldModifiedDroppedAttributesCount) != 0) { + // Encode DroppedAttributesCount + this.droppedAttributesCountEncoder.encode(val.droppedAttributesCount); + } + + if ((fieldMask & LogRecord.fieldModifiedFlags) != 0) { + // Encode Flags + this.flagsEncoder.encode(val.flags); + } + + if ((fieldMask & LogRecord.fieldModifiedTraceID) != 0) { + // Encode TraceID + this.traceIDEncoder.encode(val.traceID); + } + + if ((fieldMask & LogRecord.fieldModifiedSpanID) != 0) { + // Encode SpanID + this.spanIDEncoder.encode(val.spanID); + } + + if ((fieldMask & LogRecord.fieldModifiedEventName) != 0) { + // Encode EventName + this.eventNameEncoder.encode(val.eventName); + } + + // Account written bits in the limiter. + int newLen = this.buf.bitCount(); + this.limiter.addFrameBits(newLen - oldLen); + + // Mark all fields non-modified so that next encode() correctly + // encodes only fields that change after this. + val.modifiedFields.mask = 0; + } + + // collectColumns collects all buffers from all encoders into buf. + public void collectColumns(WriteColumnSet columnSet) { + columnSet.setBits(this.buf); + int colIdx = 0; + + // Collect TimeUnixNano field. + if (this.fieldCount <= 0) { + return; // TimeUnixNano and subsequent fields are skipped. + } + + timeUnixNanoEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + + // Collect ObservedTimeUnixNano field. + if (this.fieldCount <= 1) { + return; // ObservedTimeUnixNano and subsequent fields are skipped. + } + + observedTimeUnixNanoEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + + // Collect SeverityNumber field. + if (this.fieldCount <= 2) { + return; // SeverityNumber and subsequent fields are skipped. + } + + severityNumberEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + + // Collect SeverityText field. + if (this.fieldCount <= 3) { + return; // SeverityText and subsequent fields are skipped. + } + + severityTextEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + + // Collect Body field. + if (this.fieldCount <= 4) { + return; // Body and subsequent fields are skipped. + } + if (!isBodyRecursive) { + bodyEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + } + + // Collect Attributes field. + if (this.fieldCount <= 5) { + return; // Attributes and subsequent fields are skipped. + } + if (!isAttributesRecursive) { + attributesEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + } + + // Collect DroppedAttributesCount field. + if (this.fieldCount <= 6) { + return; // DroppedAttributesCount and subsequent fields are skipped. + } + + droppedAttributesCountEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + + // Collect Flags field. + if (this.fieldCount <= 7) { + return; // Flags and subsequent fields are skipped. + } + + flagsEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + + // Collect TraceID field. + if (this.fieldCount <= 8) { + return; // TraceID and subsequent fields are skipped. + } + + traceIDEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + + // Collect SpanID field. + if (this.fieldCount <= 9) { + return; // SpanID and subsequent fields are skipped. + } + + spanIDEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + + // Collect EventName field. + if (this.fieldCount <= 10) { + return; // EventName and subsequent fields are skipped. + } + + eventNameEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + + } +} diff --git a/java/src/main/java/com/example/otelstef/Logs.java b/java/src/main/java/com/example/otelstef/Logs.java new file mode 100644 index 00000000..db4a141c --- /dev/null +++ b/java/src/main/java/com/example/otelstef/Logs.java @@ -0,0 +1,301 @@ +// Code generated by stefc. DO NOT EDIT. +// Logs Java class generated from template +package com.example.otelstef; + +import net.stef.BytesValue; +import net.stef.StringValue; +import net.stef.Types; +import net.stef.schema.WireSchema; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.*; + +public class Logs { + // Field values. + + Envelope envelope; + Resource resource; + Scope scope; + LogRecord log; + + // modifiedFields keeps track of which fields are modified. + final ModifiedFields modifiedFields = new ModifiedFields(); + + public static final String StructName = "Logs"; + + // Bitmasks for "modified" flags for each field. + + public static final long fieldModifiedEnvelope = 1 << 0; + public static final long fieldModifiedResource = 1 << 1; + public static final long fieldModifiedScope = 1 << 2; + public static final long fieldModifiedLog = 1 << 3; + + + + public Logs() { + init(null, 0); + } + + Logs(ModifiedFields parentModifiedFields, long parentModifiedBit) { + init(parentModifiedFields, parentModifiedBit); + } + + private void init(ModifiedFields parentModifiedFields, long parentModifiedBit) { + modifiedFields.parent = parentModifiedFields; + modifiedFields.parentBit = parentModifiedBit; + + envelope = new Envelope(modifiedFields, fieldModifiedEnvelope); + resource = new Resource(modifiedFields, fieldModifiedResource); + scope = new Scope(modifiedFields, fieldModifiedScope); + log = new LogRecord(modifiedFields, fieldModifiedLog); + } + + void reset() { + + if (envelope != null) { + envelope.reset(); + } + resource = new Resource(modifiedFields, fieldModifiedResource); + scope = new Scope(modifiedFields, fieldModifiedScope); + if (log != null) { + log.reset(); + } + } + + + public Envelope getEnvelope() { + return this.envelope; + } + + private void markEnvelopeModified() { + this.modifiedFields.markModified(fieldModifiedEnvelope); + } + + // isEnvelopeModified returns true if the value of Envelope field was modified since + // Logs was created, encoded or decoded. If the field is modified + // it will be encoded by the next Write() operation. If the field is decoded by the + // next Read() operation the modified flag will be set. + public boolean isEnvelopeModified() { + return (this.modifiedFields.mask & fieldModifiedEnvelope) != 0; + } + + public Resource getResource() { + return this.resource; + } + + private void markResourceModified() { + this.modifiedFields.markModified(fieldModifiedResource); + } + + // isResourceModified returns true if the value of Resource field was modified since + // Logs was created, encoded or decoded. If the field is modified + // it will be encoded by the next Write() operation. If the field is decoded by the + // next Read() operation the modified flag will be set. + public boolean isResourceModified() { + return (this.modifiedFields.mask & fieldModifiedResource) != 0; + } + + public Scope getScope() { + return this.scope; + } + + private void markScopeModified() { + this.modifiedFields.markModified(fieldModifiedScope); + } + + // isScopeModified returns true if the value of Scope field was modified since + // Logs was created, encoded or decoded. If the field is modified + // it will be encoded by the next Write() operation. If the field is decoded by the + // next Read() operation the modified flag will be set. + public boolean isScopeModified() { + return (this.modifiedFields.mask & fieldModifiedScope) != 0; + } + + public LogRecord getLog() { + return this.log; + } + + private void markLogModified() { + this.modifiedFields.markModified(fieldModifiedLog); + } + + // isLogModified returns true if the value of Log field was modified since + // Logs was created, encoded or decoded. If the field is modified + // it will be encoded by the next Write() operation. If the field is decoded by the + // next Read() operation the modified flag will be set. + public boolean isLogModified() { + return (this.modifiedFields.mask & fieldModifiedLog) != 0; + } + + + void markModifiedRecursively() { + envelope.markModifiedRecursively(); + resource.markModifiedRecursively(); + scope.markModifiedRecursively(); + log.markModifiedRecursively(); + modifiedFields.mask = + fieldModifiedEnvelope | + fieldModifiedResource | + fieldModifiedScope | + fieldModifiedLog | 0; + } + + void markUnmodifiedRecursively() { + if (isEnvelopeModified()) { + envelope.markUnmodifiedRecursively(); + } + if (isResourceModified()) { + resource.markUnmodifiedRecursively(); + } + if (isScopeModified()) { + scope.markUnmodifiedRecursively(); + } + if (isLogModified()) { + log.markUnmodifiedRecursively(); + } + modifiedFields.mask = 0; + } + + public Logs clone() { + Logs cpy = new Logs(); + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); + return cpy; + } + + // ByteSize returns approximate memory usage in bytes. Used to calculate memory used by dictionaries. + int byteSize() { + int size = 0; // TODO: calculate the size of this object. + size += this.envelope.byteSize(); + size += this.resource.byteSize(); + size += this.scope.byteSize(); + size += this.log.byteSize(); + return size; + } + + // Performs a deep copy from src to dst. + public void copyFrom(Logs src) { + envelope.copyFrom(src.envelope); + resource.copyFrom(src.resource); + scope.copyFrom(src.scope); + log.copyFrom(src.log); + } + + // equals performs deep comparison and returns true if struct is equal to val. + public boolean equals(Logs right) { + // Compare Envelope field. + if (!this.envelope.equals(right.envelope)) { + return false; + } + // Compare Resource field. + if (!this.resource.equals(right.resource)) { + return false; + } + // Compare Scope field. + if (!this.scope.equals(right.scope)) { + return false; + } + // Compare Log field. + if (!this.log.equals(right.log)) { + return false; + } + return true; + } + + public static boolean equals(Logs left, Logs right) { + return left.equals(right); + } + + // compare performs deep comparison and returns an integer that + // will be 0 if left == right, negative if left < right, positive if left > right. + public static int compare(Logs left, Logs right) { + if (left == null) { + if (right == null) { + return 0; + } + return -1; + } + if (right == null) { + return 1; + } + int c; + + // Compare Envelope field. + c = Envelope.compare(left.envelope, right.envelope); + if (c != 0) { + return c; + } + + // Compare Resource field. + c = Resource.compare(left.resource, right.resource); + if (c != 0) { + return c; + } + + // Compare Scope field. + c = Scope.compare(left.scope, right.scope); + if (c != 0) { + return c; + } + + // Compare Log field. + c = LogRecord.compare(left.log, right.log); + if (c != 0) { + return c; + } + + return 0; + } + + // mutateRandom mutates fields in a random, deterministic manner using random as a deterministic generator. + void mutateRandom(Random random, CommonMutateRandomLimiter limiter) { + final int fieldCount = Math.max(4,2); // At least 2 to ensure we don't recurse infinitely if there is only 1 field. + + if (random.nextInt(fieldCount) == 0) { + this.envelope.mutateRandom(random, limiter); + } + + if (random.nextInt(fieldCount) == 0) { + this.resource.mutateRandom(random, limiter); + } + + if (random.nextInt(fieldCount) == 0) { + this.scope.mutateRandom(random, limiter); + } + + if (random.nextInt(fieldCount) == 0) { + this.log.mutateRandom(random, limiter); + } + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) { + return false; + } + return equals((Logs)o); + } + + @Override + public int hashCode() { + return Objects.hash( + envelope, + resource, + scope, + log + ); + } + + + static final byte[] wireSchemaBytes = new byte[]{ 0x06, 0x04, 0x01, 0x03, 0x07, 0x05, 0x0B }; + + static WireSchema wireSchema() throws IOException { + WireSchema w = new WireSchema(); + w.deserialize(new ByteArrayInputStream(wireSchemaBytes)); + return w; + } + +} diff --git a/java/src/main/java/com/example/otelstef/LogsDecoder.java b/java/src/main/java/com/example/otelstef/LogsDecoder.java new file mode 100644 index 00000000..46b07277 --- /dev/null +++ b/java/src/main/java/com/example/otelstef/LogsDecoder.java @@ -0,0 +1,207 @@ +// Code generated by stefc. DO NOT EDIT. +// LogsDecoder implements decoding of Logs +package com.example.otelstef; + +import net.stef.BitsReader; +import net.stef.Helper; +import net.stef.ReadColumnSet; +import net.stef.ReadableColumn; +import net.stef.codecs.*; + +import java.io.IOException; + +class LogsDecoder { + private final BitsReader buf = new BitsReader(); + private ReadableColumn column; + private int fieldCount; + + + + private EnvelopeDecoder envelopeDecoder; + private boolean isEnvelopeRecursive = false; // Indicates Envelope field's type is recursive. + private ResourceDecoder resourceDecoder; + private boolean isResourceRecursive = false; // Indicates Resource field's type is recursive. + private ScopeDecoder scopeDecoder; + private boolean isScopeRecursive = false; // Indicates Scope field's type is recursive. + private LogRecordDecoder logDecoder; + private boolean isLogRecursive = false; // Indicates Log field's type is recursive. + + + // Init is called once in the lifetime of the stream. + public void init(ReaderState state, ReadColumnSet columns) throws IOException { + // Remember this encoder in the state so that we can detect recursion. + if (state.LogsDecoder != null) { + throw new IllegalStateException("cannot initialize LogsDecoder: already initialized"); + } + state.LogsDecoder = this; + + try { + fieldCount = state.getStructFieldCounts().getLogsFieldCount(); + column = columns.getColumn(); + + if (this.fieldCount <= 0) { + return; // Envelope and subsequent fields are skipped. + } + if (state.EnvelopeDecoder != null) { + // Recursion detected, use the existing decoder. + envelopeDecoder = state.EnvelopeDecoder; + isEnvelopeRecursive = true; // Mark that we are using a recursive decoder. + } else { + envelopeDecoder = new EnvelopeDecoder(); + envelopeDecoder.init(state, columns.addSubColumn()); + } + if (this.fieldCount <= 1) { + return; // Resource and subsequent fields are skipped. + } + if (state.ResourceDecoder != null) { + // Recursion detected, use the existing decoder. + resourceDecoder = state.ResourceDecoder; + isResourceRecursive = true; // Mark that we are using a recursive decoder. + } else { + resourceDecoder = new ResourceDecoder(); + resourceDecoder.init(state, columns.addSubColumn()); + } + if (this.fieldCount <= 2) { + return; // Scope and subsequent fields are skipped. + } + if (state.ScopeDecoder != null) { + // Recursion detected, use the existing decoder. + scopeDecoder = state.ScopeDecoder; + isScopeRecursive = true; // Mark that we are using a recursive decoder. + } else { + scopeDecoder = new ScopeDecoder(); + scopeDecoder.init(state, columns.addSubColumn()); + } + if (this.fieldCount <= 3) { + return; // Log and subsequent fields are skipped. + } + if (state.LogRecordDecoder != null) { + // Recursion detected, use the existing decoder. + logDecoder = state.LogRecordDecoder; + isLogRecursive = true; // Mark that we are using a recursive decoder. + } else { + logDecoder = new LogRecordDecoder(); + logDecoder.init(state, columns.addSubColumn()); + } + } finally { + state.LogsDecoder = null; + } + } + + // continueDecoding is called at the start of the frame to continue decoding column data. + // This should set the decoder's source buffer, so the new decoding continues from + // the supplied column data. This should NOT reset the internal state of the decoder, + // since columns can cross frame boundaries and the new column data is considered + // continuation of that same column in the previous frame. + public void continueDecoding() { + this.buf.reset(this.column.getData()); + + if (this.fieldCount <= 0) { + return; // Envelope and subsequent fields are skipped. + } + + if (!isEnvelopeRecursive) { + envelopeDecoder.continueDecoding(); + } + + if (this.fieldCount <= 1) { + return; // Resource and subsequent fields are skipped. + } + + if (!isResourceRecursive) { + resourceDecoder.continueDecoding(); + } + + if (this.fieldCount <= 2) { + return; // Scope and subsequent fields are skipped. + } + + if (!isScopeRecursive) { + scopeDecoder.continueDecoding(); + } + + if (this.fieldCount <= 3) { + return; // Log and subsequent fields are skipped. + } + + if (!isLogRecursive) { + logDecoder.continueDecoding(); + } + + } + + public void reset() { + + if (fieldCount <= 0) { + // Envelope and all subsequent fields are skipped. + return; + } + if (!isEnvelopeRecursive) { + envelopeDecoder.reset(); + } + if (fieldCount <= 1) { + // Resource and all subsequent fields are skipped. + return; + } + if (!isResourceRecursive) { + resourceDecoder.reset(); + } + if (fieldCount <= 2) { + // Scope and all subsequent fields are skipped. + return; + } + if (!isScopeRecursive) { + scopeDecoder.reset(); + } + if (fieldCount <= 3) { + // Log and all subsequent fields are skipped. + return; + } + if (!isLogRecursive) { + logDecoder.reset(); + } + } + + public Logs decode(Logs dstPtr) throws IOException { + Logs val = dstPtr; + // Read bits that indicate which fields follow. + val.modifiedFields.mask = buf.readBits(fieldCount); + + + if ((val.modifiedFields.mask & Logs.fieldModifiedEnvelope) != 0) { + // Field is changed. + if (val.envelope == null) { + val.envelope = new Envelope(val.modifiedFields, Logs.fieldModifiedEnvelope); + } + val.envelope = envelopeDecoder.decode(val.envelope); + } + + if ((val.modifiedFields.mask & Logs.fieldModifiedResource) != 0) { + // Field is changed. + if (val.resource == null) { + val.resource = new Resource(val.modifiedFields, Logs.fieldModifiedResource); + } + val.resource = resourceDecoder.decode(val.resource); + } + + if ((val.modifiedFields.mask & Logs.fieldModifiedScope) != 0) { + // Field is changed. + if (val.scope == null) { + val.scope = new Scope(val.modifiedFields, Logs.fieldModifiedScope); + } + val.scope = scopeDecoder.decode(val.scope); + } + + if ((val.modifiedFields.mask & Logs.fieldModifiedLog) != 0) { + // Field is changed. + if (val.log == null) { + val.log = new LogRecord(val.modifiedFields, Logs.fieldModifiedLog); + } + val.log = logDecoder.decode(val.log); + } + + + return val; + } +} + diff --git a/java/src/main/java/com/example/otelstef/LogsEncoder.java b/java/src/main/java/com/example/otelstef/LogsEncoder.java new file mode 100644 index 00000000..d54a6af4 --- /dev/null +++ b/java/src/main/java/com/example/otelstef/LogsEncoder.java @@ -0,0 +1,247 @@ +// Code generated by stefc. DO NOT EDIT. +// LogsEncoder implements encoding of Logs +package com.example.otelstef; + +import net.stef.BitsWriter; +import net.stef.Helper; +import net.stef.SizeLimiter; +import net.stef.WriteColumnSet; +import net.stef.codecs.*; + +import java.io.IOException; + +class LogsEncoder { + private BitsWriter buf = new BitsWriter(); + private SizeLimiter limiter; + + // forceModifiedFields is set to true if the next encoding operation + // must write all fields, whether they are modified or not. + // This is used after frame restarts so that the data can be decoded + // from the frame start. + private boolean forceModifiedFields; + + + private EnvelopeEncoder envelopeEncoder; + private boolean isEnvelopeRecursive = false; // Indicates Envelope field's type is recursive. + private ResourceEncoder resourceEncoder; + private boolean isResourceRecursive = false; // Indicates Resource field's type is recursive. + private ScopeEncoder scopeEncoder; + private boolean isScopeRecursive = false; // Indicates Scope field's type is recursive. + private LogRecordEncoder logEncoder; + private boolean isLogRecursive = false; // Indicates Log field's type is recursive. + + + private long keepFieldMask; + private int fieldCount; + + + public void init(WriterState state, WriteColumnSet columns) throws IOException { + // Remember this encoder in the state so that we can detect recursion. + if (state.LogsEncoder != null) { + throw new IllegalStateException("cannot initialize LogsEncoder: already initialized"); + } + state.LogsEncoder = this; + + try { + limiter = state.getLimiter(); + + fieldCount = state.getStructFieldCounts().getLogsFieldCount(); + keepFieldMask = ~((~0L) << fieldCount); + // Init encoder for Envelope field. + if (fieldCount <= 0) { + return; // Envelope and subsequent fields are skipped. + } + if (state.EnvelopeEncoder != null) { + // Recursion detected, use the existing encoder. + envelopeEncoder = state.EnvelopeEncoder; + isEnvelopeRecursive = true; + } else { + envelopeEncoder = new EnvelopeEncoder(); + envelopeEncoder.init(state, columns.addSubColumn()); + } + // Init encoder for Resource field. + if (fieldCount <= 1) { + return; // Resource and subsequent fields are skipped. + } + if (state.ResourceEncoder != null) { + // Recursion detected, use the existing encoder. + resourceEncoder = state.ResourceEncoder; + isResourceRecursive = true; + } else { + resourceEncoder = new ResourceEncoder(); + resourceEncoder.init(state, columns.addSubColumn()); + } + // Init encoder for Scope field. + if (fieldCount <= 2) { + return; // Scope and subsequent fields are skipped. + } + if (state.ScopeEncoder != null) { + // Recursion detected, use the existing encoder. + scopeEncoder = state.ScopeEncoder; + isScopeRecursive = true; + } else { + scopeEncoder = new ScopeEncoder(); + scopeEncoder.init(state, columns.addSubColumn()); + } + // Init encoder for Log field. + if (fieldCount <= 3) { + return; // Log and subsequent fields are skipped. + } + if (state.LogRecordEncoder != null) { + // Recursion detected, use the existing encoder. + logEncoder = state.LogRecordEncoder; + isLogRecursive = true; + } else { + logEncoder = new LogRecordEncoder(); + logEncoder.init(state, columns.addSubColumn()); + } + } finally { + state.LogsEncoder = null; + } + } + + public void reset() { + // Since we are resetting the state of encoder make sure the next encode() + // call forcefully writes all fields and does not attempt to skip. + this.forceModifiedFields = true; + + if (fieldCount <= 0) { + return; // Envelope and all subsequent fields are skipped. + } + + if (!isEnvelopeRecursive) { + envelopeEncoder.reset(); + } + + if (fieldCount <= 1) { + return; // Resource and all subsequent fields are skipped. + } + + if (!isResourceRecursive) { + resourceEncoder.reset(); + } + + if (fieldCount <= 2) { + return; // Scope and all subsequent fields are skipped. + } + + if (!isScopeRecursive) { + scopeEncoder.reset(); + } + + if (fieldCount <= 3) { + return; // Log and all subsequent fields are skipped. + } + + if (!isLogRecursive) { + logEncoder.reset(); + } + + } + + // encode encodes val into buf + public void encode(Logs val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(Logs val, boolean forceAllFields) throws IOException { + int oldLen = this.buf.bitCount(); + + + + // Mask that describes what fields are encoded. Start with all modified fields. + long fieldMask = val.modifiedFields.mask; + // On an encoder restart or when requested by a containing collection, + // force writing of all fields. + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { + fieldMask = + Logs.fieldModifiedEnvelope | + Logs.fieldModifiedResource | + Logs.fieldModifiedScope | + Logs.fieldModifiedLog | 0L; + this.forceModifiedFields = false; + } + + // Only write fields that we want to write. See init() for keepFieldMask. + fieldMask &= this.keepFieldMask; + + // Write bits to indicate which fields follow. + this.buf.writeBits(fieldMask, this.fieldCount); + + // Encode modified, present fields. + + if ((fieldMask & Logs.fieldModifiedEnvelope) != 0) { + // Encode Envelope + this.envelopeEncoder.encode(val.envelope, forceFields); + } + + if ((fieldMask & Logs.fieldModifiedResource) != 0) { + // Encode Resource + this.resourceEncoder.encode(val.resource, forceFields); + } + + if ((fieldMask & Logs.fieldModifiedScope) != 0) { + // Encode Scope + this.scopeEncoder.encode(val.scope, forceFields); + } + + if ((fieldMask & Logs.fieldModifiedLog) != 0) { + // Encode Log + this.logEncoder.encode(val.log, forceFields); + } + + // Account written bits in the limiter. + int newLen = this.buf.bitCount(); + this.limiter.addFrameBits(newLen - oldLen); + + // Mark all fields non-modified so that next encode() correctly + // encodes only fields that change after this. + val.modifiedFields.mask = 0; + } + + // collectColumns collects all buffers from all encoders into buf. + public void collectColumns(WriteColumnSet columnSet) { + columnSet.setBits(this.buf); + int colIdx = 0; + + // Collect Envelope field. + if (this.fieldCount <= 0) { + return; // Envelope and subsequent fields are skipped. + } + if (!isEnvelopeRecursive) { + envelopeEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + } + + // Collect Resource field. + if (this.fieldCount <= 1) { + return; // Resource and subsequent fields are skipped. + } + if (!isResourceRecursive) { + resourceEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + } + + // Collect Scope field. + if (this.fieldCount <= 2) { + return; // Scope and subsequent fields are skipped. + } + if (!isScopeRecursive) { + scopeEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + } + + // Collect Log field. + if (this.fieldCount <= 3) { + return; // Log and subsequent fields are skipped. + } + if (!isLogRecursive) { + logEncoder.collectColumns(columnSet.at(colIdx)); + colIdx++; + } + + } +} diff --git a/java/src/main/java/com/example/otelstef/LogsReader.java b/java/src/main/java/com/example/otelstef/LogsReader.java new file mode 100644 index 00000000..0485120e --- /dev/null +++ b/java/src/main/java/com/example/otelstef/LogsReader.java @@ -0,0 +1,108 @@ +// Code generated by stefc. DO NOT EDIT. +package com.example.otelstef; + +import net.stef.BaseReader; +import net.stef.FixedHeader; +import net.stef.FrameFlags; +import net.stef.ReadOptions; +import net.stef.ReadResult; +import net.stef.schema.WireSchema; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +// LogsReader allows reading records of Logs +// in STEF format from an input stream. +public class LogsReader { + private final BaseReader base; + private final LogsDecoder decoder = new LogsDecoder(); + private final ReaderState state = new ReaderState(); + + // Record contains the record that was just read by the last read() operation. + // The next read() will overwrite the record. + // Do not modify this field externally. Modifying this field will result in + // incorrect decoding of subsequent records. + public final Logs record = new Logs(); + + public LogsReader(InputStream source) throws IOException { + base = new BaseReader(new BufferedInputStream(source, 64 * 1024)); + initSchema(); + } + + private void initSchema() throws IOException { + WireSchema ownSchema = Logs.wireSchema(); + base.readVarHeader(ownSchema); + state.init(base.getSchema()); + decoder.init(state, base.readBufs.columns); + + if (!state.getStructFieldCounts().allFetched()) { + throw new IOException("override schema iterator is not done, decoded data schema is likely incompatible"); + } + } + + public FixedHeader getHeader() { + return base.fixedHeader; + } + + public Map getUserData() { + return base.varHeader.getUserData(); + } + + public long getRecordCount() { + return base.recordCount; + } + + private void nextFrame() throws IOException { + int frameFlags = base.nextFrame(); + if ((frameFlags & FrameFlags.RestartDictionaries) != 0) { + // The frame that has just started indicates that the dictionaries + // must be restarted. Reset all dictionaries. + state.resetDicts(); + } + if ((frameFlags & FrameFlags.RestartCodecs) != 0) { + // The frame that has just started indicates that the decoders + // must be restarted. + decoder.reset(); + } + decoder.continueDecoding(); + } + + // Read the next record. After read() returns successfully the record + // will be accessible in LogsReader.record field. + // + // If ReadOptions.tillEndOfFrame=true and end of the current + // frame is reached read() will return EndOfFrame. Calling read() + // after that with ReadOptions.tillEndOfFrame=false will correctly load + // the next frame (if any) and read the next record from the frame. + // The very first read() must use ReadOptions.tillEndOfFrame=false + // otherwise EndOfFrameException will be thrown since no frames are + // initially loaded in the Reader. See more details in ReadOptions. + // + // If read() encounters a decoding error a IOException will be thrown. + // If underlying source InputStream throws any error then read() will + // either throw that error or a decoding error if source InputStream + // returned an error prematurely while more data was expected in STEF stream. + // + // For well-formed streams that don't encounter decoding errors read() will + // throw EOFException once end of the underlying source InputStream is reached + // (assuming InputStream throws EOFException itself). + public ReadResult read(int opts) throws IOException { + while (base.frameRecordCount == 0) { + if ((opts&ReadOptions.tillEndOfFrame)!=0) { + return ReadResult.EndOfFrame; + } + nextFrame(); + } + base.frameRecordCount--; + base.recordCount++; + decoder.decode(record); + return ReadResult.Success; + } + + // Returns the schema of the STEF stream being read. + public WireSchema getSchema() { + return base.getSchema(); + } +} diff --git a/java/src/main/java/com/example/otelstef/LogsWriter.java b/java/src/main/java/com/example/otelstef/LogsWriter.java new file mode 100644 index 00000000..e94ff88c --- /dev/null +++ b/java/src/main/java/com/example/otelstef/LogsWriter.java @@ -0,0 +1,142 @@ +// Code generated by stefc. DO NOT EDIT. +package com.example.otelstef; + +import net.stef.Constants; +import net.stef.ChunkWriter; +import net.stef.FrameEncoder; +import net.stef.FrameFlags; +import net.stef.Serde; +import net.stef.VarHeader; +import net.stef.WriteBufs; +import net.stef.WriterOptions; +import net.stef.schema.Compatibility; +import net.stef.schema.WireSchema; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +// LogsWriter allows writing records of Logs +// in STEF format to an output stream. +public class LogsWriter { + // The next record to be written. Modify this record and call write() + // to write a record. + public final Logs record; + + private WriterOptions opts; + private ChunkWriter dst; + private FrameEncoder frameEncoder; + private LogsEncoder encoder; + private WriterState state; + private WriteBufs writeBufs; + private long frameRecordCount; + private long recordCount; + + public LogsWriter(ChunkWriter dst, WriterOptions opts) throws IOException { + this.dst = dst; + this.opts = opts; + this.record = new Logs(); + this.state = new WriterState(); + this.encoder = new LogsEncoder(); + this.writeBufs = new WriteBufs(); + this.frameEncoder = new FrameEncoder(); + + if (this.opts.getSchema() != null) { + WireSchema ownSchema = Logs.wireSchema(); + if (ownSchema.compatible(this.opts.getSchema()) == Compatibility.Incompatible) { + throw new IOException("schema is not compatible with Writer"); + } + } + + state.init(opts); + encoder.init(state, writeBufs.columns); + + if (!state.getStructFieldCounts().allFetched()) { + throw new IOException("override schema iterator is not done, likely supplied schema override does not match the generated code"); + } + + frameEncoder.init(dst, opts.getCompression()); + writeFixedHeader(); + writeVarHeader(); + } + + private void writeFixedHeader() throws IOException { + ByteArrayOutputStream hdrTail = new ByteArrayOutputStream(); + byte versionAndType = Constants.HdrFormatVersion; + hdrTail.write(versionAndType); + byte flags = (byte) (this.opts.getCompression().getValue() & Constants.HdrFlagsCompressionMethod); + hdrTail.write(flags); + long hdrTailSize = hdrTail.size(); + + ByteArrayOutputStream hdrFull = new ByteArrayOutputStream(); + hdrFull.write(Constants.HdrSignature); + Serde.writeUvarint(hdrTailSize, hdrFull); + hdrFull.write(hdrTail.toByteArray()); + + this.dst.writeChunk(hdrFull.toByteArray(), null); + } + + private void writeVarHeader() throws IOException { + VarHeader hdr = new VarHeader(); + if (opts.isIncludeDescriptor()) { + if (opts.getSchema() != null) { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + opts.getSchema().serialize(buf); + hdr.setSchemaWireBytes(buf.toByteArray()); + } else { + hdr.setSchemaWireBytes(Logs.wireSchemaBytes); + } + } + if (opts.getUserData() != null && !opts.getUserData().isEmpty()) { + hdr.setUserData(opts.getUserData()); + } + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + hdr.serialize(buf); + frameEncoder.write(buf.toByteArray()); + frameEncoder.closeFrame(); + frameEncoder.openFrame(0); + } + + public void write() throws IOException { + encoder.encode(this.record); + frameRecordCount++; + int nextFrameFlags = this.opts.getFrameRestartFlags(); + boolean restartFrame = false; + if (state.getLimiter().isDictLimitReached() || (nextFrameFlags & FrameFlags.RestartDictionaries) != 0) { + state.resetDicts(); + nextFrameFlags = this.opts.getFrameRestartFlags() | FrameFlags.RestartDictionaries; + restartFrame = true; + } + if (state.getLimiter().isFrameLimitReached()) { + restartFrame = true; + } + if (restartFrame) { + restartFrame(nextFrameFlags); + } + recordCount++; + } + + public long getRecordCount() { + return this.recordCount; + } + + private void restartFrame(int nextFrameFlags) throws IOException { + if ((nextFrameFlags & FrameFlags.RestartCodecs) != 0) { + encoder.reset(); + } + + Serde.writeUvarint(frameRecordCount, frameEncoder); + frameRecordCount = 0; + encoder.collectColumns(writeBufs.columns); + writeBufs.writeTo(frameEncoder); + frameEncoder.closeFrame(); + frameEncoder.openFrame(nextFrameFlags); + state.getLimiter().resetFrameSize(); + } + + public void flush() throws IOException { + if (frameRecordCount == 0) { + return; + } + restartFrame(opts.getFrameRestartFlags()); + } +} diff --git a/java/src/main/java/com/example/otelstef/Metric.java b/java/src/main/java/com/example/otelstef/Metric.java index 374754a4..39e42bd4 100644 --- a/java/src/main/java/com/example/otelstef/Metric.java +++ b/java/src/main/java/com/example/otelstef/Metric.java @@ -283,14 +283,8 @@ void markUnmodifiedRecursively() { public Metric clone() { Metric cpy = new Metric(); - cpy.name = this.name; - cpy.description = this.description; - cpy.unit = this.unit; - cpy.type_ = this.type_; - cpy.metadata = this.metadata.clone(); - cpy.histogramBounds = this.histogramBounds.clone(); - cpy.aggregationTemporality = this.aggregationTemporality; - cpy.monotonic = this.monotonic; + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/MetricEncoder.java b/java/src/main/java/com/example/otelstef/MetricEncoder.java index 2290a3e6..bb0f3e0a 100644 --- a/java/src/main/java/com/example/otelstef/MetricEncoder.java +++ b/java/src/main/java/com/example/otelstef/MetricEncoder.java @@ -172,6 +172,12 @@ public void reset() { // encode encodes val into buf public void encode(Metric val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(Metric val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); @@ -203,9 +209,10 @@ public void encode(Metric val) throws IOException { // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = Metric.fieldModifiedName | Metric.fieldModifiedDescription | @@ -215,6 +222,7 @@ public void encode(Metric val) throws IOException { Metric.fieldModifiedHistogramBounds | Metric.fieldModifiedAggregationTemporality | Metric.fieldModifiedMonotonic | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -247,12 +255,12 @@ public void encode(Metric val) throws IOException { if ((fieldMask & Metric.fieldModifiedMetadata) != 0) { // Encode Metadata - this.metadataEncoder.encode(val.metadata); + this.metadataEncoder.encode(val.metadata, forceFields); } if ((fieldMask & Metric.fieldModifiedHistogramBounds) != 0) { // Encode HistogramBounds - this.histogramBoundsEncoder.encode(val.histogramBounds); + this.histogramBoundsEncoder.encode(val.histogramBounds, forceFields); } if ((fieldMask & Metric.fieldModifiedAggregationTemporality) != 0) { @@ -347,4 +355,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/Metrics.java b/java/src/main/java/com/example/otelstef/Metrics.java index 34166579..27d96b7e 100644 --- a/java/src/main/java/com/example/otelstef/Metrics.java +++ b/java/src/main/java/com/example/otelstef/Metrics.java @@ -211,12 +211,8 @@ void markUnmodifiedRecursively() { public Metrics clone() { Metrics cpy = new Metrics(); - cpy.envelope = this.envelope.clone(); - cpy.metric = this.metric.clone(); - cpy.resource = this.resource.clone(); - cpy.scope = this.scope.clone(); - cpy.attributes = this.attributes.clone(); - cpy.point = this.point.clone(); + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/MetricsEncoder.java b/java/src/main/java/com/example/otelstef/MetricsEncoder.java index 42fdad2e..4c25d289 100644 --- a/java/src/main/java/com/example/otelstef/MetricsEncoder.java +++ b/java/src/main/java/com/example/otelstef/MetricsEncoder.java @@ -185,15 +185,22 @@ public void reset() { // encode encodes val into buf public void encode(Metrics val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(Metrics val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = Metrics.fieldModifiedEnvelope | Metrics.fieldModifiedMetric | @@ -201,6 +208,7 @@ public void encode(Metrics val) throws IOException { Metrics.fieldModifiedScope | Metrics.fieldModifiedAttributes | Metrics.fieldModifiedPoint | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -213,32 +221,32 @@ public void encode(Metrics val) throws IOException { if ((fieldMask & Metrics.fieldModifiedEnvelope) != 0) { // Encode Envelope - this.envelopeEncoder.encode(val.envelope); + this.envelopeEncoder.encode(val.envelope, forceFields); } if ((fieldMask & Metrics.fieldModifiedMetric) != 0) { // Encode Metric - this.metricEncoder.encode(val.metric); + this.metricEncoder.encode(val.metric, forceFields); } if ((fieldMask & Metrics.fieldModifiedResource) != 0) { // Encode Resource - this.resourceEncoder.encode(val.resource); + this.resourceEncoder.encode(val.resource, forceFields); } if ((fieldMask & Metrics.fieldModifiedScope) != 0) { // Encode Scope - this.scopeEncoder.encode(val.scope); + this.scopeEncoder.encode(val.scope, forceFields); } if ((fieldMask & Metrics.fieldModifiedAttributes) != 0) { // Encode Attributes - this.attributesEncoder.encode(val.attributes); + this.attributesEncoder.encode(val.attributes, forceFields); } if ((fieldMask & Metrics.fieldModifiedPoint) != 0) { // Encode Point - this.pointEncoder.encode(val.point); + this.pointEncoder.encode(val.point, forceFields); } // Account written bits in the limiter. @@ -311,4 +319,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/Point.java b/java/src/main/java/com/example/otelstef/Point.java index d39d3a94..2b6212d7 100644 --- a/java/src/main/java/com/example/otelstef/Point.java +++ b/java/src/main/java/com/example/otelstef/Point.java @@ -167,10 +167,8 @@ void markUnmodifiedRecursively() { public Point clone() { Point cpy = new Point(); - cpy.startTimestamp = this.startTimestamp; - cpy.timestamp = this.timestamp; - cpy.value = this.value.clone(); - cpy.exemplars = this.exemplars.clone(); + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/PointEncoder.java b/java/src/main/java/com/example/otelstef/PointEncoder.java index cbb33af2..fd55172b 100644 --- a/java/src/main/java/com/example/otelstef/PointEncoder.java +++ b/java/src/main/java/com/example/otelstef/PointEncoder.java @@ -121,20 +121,28 @@ public void reset() { // encode encodes val into buf public void encode(Point val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(Point val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = Point.fieldModifiedStartTimestamp | Point.fieldModifiedTimestamp | Point.fieldModifiedValue | Point.fieldModifiedExemplars | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -157,12 +165,12 @@ public void encode(Point val) throws IOException { if ((fieldMask & Point.fieldModifiedValue) != 0) { // Encode Value - this.valueEncoder.encode(val.value); + this.valueEncoder.encode(val.value, forceFields); } if ((fieldMask & Point.fieldModifiedExemplars) != 0) { // Encode Exemplars - this.exemplarsEncoder.encode(val.exemplars); + this.exemplarsEncoder.encode(val.exemplars, forceFields); } // Account written bits in the limiter. @@ -215,4 +223,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/PointValue.java b/java/src/main/java/com/example/otelstef/PointValue.java index 3a60e7f7..a4c9ee93 100644 --- a/java/src/main/java/com/example/otelstef/PointValue.java +++ b/java/src/main/java/com/example/otelstef/PointValue.java @@ -181,12 +181,8 @@ public SummaryValue getSummary() { // Clone returns a deep copy of this oneof. public PointValue clone() { PointValue cpy = new PointValue(); - cpy.typ = this.typ; - cpy.int64 = this.int64; - cpy.float64 = this.float64; - cpy.histogram = this.histogram.clone(); - cpy.expHistogram = this.expHistogram.clone(); - cpy.summary = this.summary.clone(); + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } @@ -408,13 +404,19 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash( - int64, - float64, - histogram, - expHistogram, - summary, - typ - ); + switch (this.typ) { + case TypeInt64: + return Objects.hash(typ, int64); + case TypeFloat64: + return Objects.hash(typ, float64); + case TypeHistogram: + return Objects.hash(typ, histogram); + case TypeExpHistogram: + return Objects.hash(typ, expHistogram); + case TypeSummary: + return Objects.hash(typ, summary); + default: + return Objects.hash(typ); + } } } diff --git a/java/src/main/java/com/example/otelstef/PointValueEncoder.java b/java/src/main/java/com/example/otelstef/PointValueEncoder.java index 508abd9b..3ab36a76 100644 --- a/java/src/main/java/com/example/otelstef/PointValueEncoder.java +++ b/java/src/main/java/com/example/otelstef/PointValueEncoder.java @@ -135,6 +135,12 @@ public void reset() { // Encode encodes val into buf public void encode(PointValue val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf and propagates forced full encoding through + // the selected non-primitive value. + public void encode(PointValue val, boolean forceAllFields) throws IOException { PointValue.Type typ = val.typ; if (typ.getValue() > fieldCount) { // The current field type is not supported in target schema. Encode the type as None. @@ -159,15 +165,15 @@ public void encode(PointValue val) throws IOException { break; case TypeHistogram: // Encode Histogram - histogramEncoder.encode(val.histogram); + histogramEncoder.encode(val.histogram, forceAllFields); break; case TypeExpHistogram: // Encode ExpHistogram - expHistogramEncoder.encode(val.expHistogram); + expHistogramEncoder.encode(val.expHistogram, forceAllFields); break; case TypeSummary: // Encode Summary - summaryEncoder.encode(val.summary); + summaryEncoder.encode(val.summary, forceAllFields); break; } } diff --git a/java/src/main/java/com/example/otelstef/QuantileValue.java b/java/src/main/java/com/example/otelstef/QuantileValue.java index 14d17a63..bd574777 100644 --- a/java/src/main/java/com/example/otelstef/QuantileValue.java +++ b/java/src/main/java/com/example/otelstef/QuantileValue.java @@ -113,8 +113,8 @@ void markUnmodifiedRecursively() { public QuantileValue clone() { QuantileValue cpy = new QuantileValue(); - cpy.quantile = this.quantile; - cpy.value = this.value; + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/QuantileValueArrayEncoder.java b/java/src/main/java/com/example/otelstef/QuantileValueArrayEncoder.java index 18e952e1..a6533f23 100644 --- a/java/src/main/java/com/example/otelstef/QuantileValueArrayEncoder.java +++ b/java/src/main/java/com/example/otelstef/QuantileValueArrayEncoder.java @@ -17,6 +17,8 @@ class QuantileValueArrayEncoder { private QuantileValueEncoder elemEncoder; private WriterState state; private boolean isRecursive = false; + // Set after reset so every element in the first non-empty array is encoded in full. + private boolean forceElements; public void init(WriterState state, WriteColumnSet columns) throws IOException { this.state = state; @@ -43,20 +45,28 @@ public void init(WriterState state, WriteColumnSet columns) throws IOException { } public void reset() { + forceElements = true; if (!isRecursive) { elemEncoder.reset(); } } public void encode(QuantileValueArray arr) throws IOException { + encode(arr, false); + } + + public void encode(QuantileValueArray arr, boolean forceAllElements) throws IOException { int oldBitLen = buf.bitCount(); // Write the length of the array. int newLen = arr.elemsLen; buf.writeUvarintCompact(newLen); - + boolean forceElementsNow = forceAllElements || forceElements; for (int i = 0; i < newLen; i++) { - elemEncoder.encode(arr.elems[i]); + elemEncoder.encode(arr.elems[i], forceElementsNow); + } + if (newLen != 0) { + forceElements = false; } // Account written bits in the limiter. @@ -71,4 +81,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } } - diff --git a/java/src/main/java/com/example/otelstef/QuantileValueEncoder.java b/java/src/main/java/com/example/otelstef/QuantileValueEncoder.java index a335ea2c..72c394b2 100644 --- a/java/src/main/java/com/example/otelstef/QuantileValueEncoder.java +++ b/java/src/main/java/com/example/otelstef/QuantileValueEncoder.java @@ -77,18 +77,26 @@ public void reset() { // encode encodes val into buf public void encode(QuantileValue val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(QuantileValue val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = QuantileValue.fieldModifiedQuantile | QuantileValue.fieldModifiedValue | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -141,4 +149,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/ReaderState.java b/java/src/main/java/com/example/otelstef/ReaderState.java index efed76c6..09285152 100644 --- a/java/src/main/java/com/example/otelstef/ReaderState.java +++ b/java/src/main/java/com/example/otelstef/ReaderState.java @@ -10,6 +10,8 @@ public class ReaderState { // Dictionaries final StringDictDecoderDict AnyValueString = new StringDictDecoderDict(); final StringDictDecoderDict AttributeKey = new StringDictDecoderDict(); + final StringDictDecoderDict LogEventName = new StringDictDecoderDict(); + final StringDictDecoderDict LogSeverityText = new StringDictDecoderDict(); final MetricDecoderDict Metric = new MetricDecoderDict(); final StringDictDecoderDict MetricDescription = new StringDictDecoderDict(); final StringDictDecoderDict MetricName = new StringDictDecoderDict(); @@ -40,6 +42,8 @@ public class ReaderState { KeyValueListDecoder KeyValueListDecoder; LinkDecoder LinkDecoder; LinkArrayDecoder LinkArrayDecoder; + LogRecordDecoder LogRecordDecoder; + LogsDecoder LogsDecoder; MetricDecoder MetricDecoder; MetricsDecoder MetricsDecoder; PointDecoder PointDecoder; @@ -58,6 +62,8 @@ public void init(WireSchema overrideSchema) { structFieldCounts.init(overrideSchema); this.AnyValueString.init(); this.AttributeKey.init(); + this.LogEventName.init(); + this.LogSeverityText.init(); this.Metric.init(); this.MetricDescription.init(); this.MetricName.init(); @@ -77,6 +83,8 @@ public void init(WireSchema overrideSchema) { public void resetDicts() { this.AnyValueString.reset(); this.AttributeKey.reset(); + this.LogEventName.reset(); + this.LogSeverityText.reset(); this.Metric.reset(); this.MetricDescription.reset(); this.MetricName.reset(); diff --git a/java/src/main/java/com/example/otelstef/Resource.java b/java/src/main/java/com/example/otelstef/Resource.java index 42fee283..90432115 100644 --- a/java/src/main/java/com/example/otelstef/Resource.java +++ b/java/src/main/java/com/example/otelstef/Resource.java @@ -140,9 +140,8 @@ void markUnmodifiedRecursively() { public Resource clone() { Resource cpy = new Resource(); - cpy.schemaURL = this.schemaURL; - cpy.attributes = this.attributes.clone(); - cpy.droppedAttributesCount = this.droppedAttributesCount; + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/ResourceEncoder.java b/java/src/main/java/com/example/otelstef/ResourceEncoder.java index 1dcf18f1..c6c3820a 100644 --- a/java/src/main/java/com/example/otelstef/ResourceEncoder.java +++ b/java/src/main/java/com/example/otelstef/ResourceEncoder.java @@ -102,6 +102,12 @@ public void reset() { // encode encodes val into buf public void encode(Resource val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(Resource val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); @@ -133,13 +139,15 @@ public void encode(Resource val) throws IOException { // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = Resource.fieldModifiedSchemaURL | Resource.fieldModifiedAttributes | Resource.fieldModifiedDroppedAttributesCount | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -157,7 +165,7 @@ public void encode(Resource val) throws IOException { if ((fieldMask & Resource.fieldModifiedAttributes) != 0) { // Encode Attributes - this.attributesEncoder.encode(val.attributes); + this.attributesEncoder.encode(val.attributes, forceFields); } if ((fieldMask & Resource.fieldModifiedDroppedAttributesCount) != 0) { @@ -206,4 +214,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/Scope.java b/java/src/main/java/com/example/otelstef/Scope.java index 0d73addf..a9d3d4f1 100644 --- a/java/src/main/java/com/example/otelstef/Scope.java +++ b/java/src/main/java/com/example/otelstef/Scope.java @@ -198,11 +198,8 @@ void markUnmodifiedRecursively() { public Scope clone() { Scope cpy = new Scope(); - cpy.name = this.name; - cpy.version = this.version; - cpy.schemaURL = this.schemaURL; - cpy.attributes = this.attributes.clone(); - cpy.droppedAttributesCount = this.droppedAttributesCount; + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/ScopeEncoder.java b/java/src/main/java/com/example/otelstef/ScopeEncoder.java index 8b596b78..d0b8ceaa 100644 --- a/java/src/main/java/com/example/otelstef/ScopeEncoder.java +++ b/java/src/main/java/com/example/otelstef/ScopeEncoder.java @@ -126,6 +126,12 @@ public void reset() { // encode encodes val into buf public void encode(Scope val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(Scope val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); @@ -157,15 +163,17 @@ public void encode(Scope val) throws IOException { // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = Scope.fieldModifiedName | Scope.fieldModifiedVersion | Scope.fieldModifiedSchemaURL | Scope.fieldModifiedAttributes | Scope.fieldModifiedDroppedAttributesCount | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -193,7 +201,7 @@ public void encode(Scope val) throws IOException { if ((fieldMask & Scope.fieldModifiedAttributes) != 0) { // Encode Attributes - this.attributesEncoder.encode(val.attributes); + this.attributesEncoder.encode(val.attributes, forceFields); } if ((fieldMask & Scope.fieldModifiedDroppedAttributesCount) != 0) { @@ -258,4 +266,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/SeverityNumber.java b/java/src/main/java/com/example/otelstef/SeverityNumber.java new file mode 100644 index 00000000..0a2580e4 --- /dev/null +++ b/java/src/main/java/com/example/otelstef/SeverityNumber.java @@ -0,0 +1,33 @@ +// Code generated by stefc. DO NOT EDIT. +package com.example.otelstef; + +public class SeverityNumber { + public static final long Unspecified = 0; + public static final long Trace = 1; + public static final long Trace2 = 2; + public static final long Trace3 = 3; + public static final long Trace4 = 4; + public static final long Debug = 5; + public static final long Debug2 = 6; + public static final long Debug3 = 7; + public static final long Debug4 = 8; + public static final long Info = 9; + public static final long Info2 = 10; + public static final long Info3 = 11; + public static final long Info4 = 12; + public static final long Warn = 13; + public static final long Warn2 = 14; + public static final long Warn3 = 15; + public static final long Warn4 = 16; + public static final long Error = 17; + public static final long Error2 = 18; + public static final long Error3 = 19; + public static final long Error4 = 20; + public static final long Fatal = 21; + public static final long Fatal2 = 22; + public static final long Fatal3 = 23; + public static final long Fatal4 = 24; + + // Private constructor to prevent instantiation + private SeverityNumber() {} +} diff --git a/java/src/main/java/com/example/otelstef/Span.java b/java/src/main/java/com/example/otelstef/Span.java index 85e0169a..3a6471a1 100644 --- a/java/src/main/java/com/example/otelstef/Span.java +++ b/java/src/main/java/com/example/otelstef/Span.java @@ -453,20 +453,8 @@ void markUnmodifiedRecursively() { public Span clone() { Span cpy = new Span(); - cpy.traceID = this.traceID; - cpy.spanID = this.spanID; - cpy.traceState = this.traceState; - cpy.parentSpanID = this.parentSpanID; - cpy.flags = this.flags; - cpy.name = this.name; - cpy.kind = this.kind; - cpy.startTimeUnixNano = this.startTimeUnixNano; - cpy.endTimeUnixNano = this.endTimeUnixNano; - cpy.attributes = this.attributes.clone(); - cpy.droppedAttributesCount = this.droppedAttributesCount; - cpy.events = this.events.clone(); - cpy.links = this.links.clone(); - cpy.status = this.status.clone(); + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/SpanEncoder.java b/java/src/main/java/com/example/otelstef/SpanEncoder.java index 41ff935f..ab53f123 100644 --- a/java/src/main/java/com/example/otelstef/SpanEncoder.java +++ b/java/src/main/java/com/example/otelstef/SpanEncoder.java @@ -261,15 +261,22 @@ public void reset() { // encode encodes val into buf public void encode(Span val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(Span val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = Span.fieldModifiedTraceID | Span.fieldModifiedSpanID | @@ -285,6 +292,7 @@ public void encode(Span val) throws IOException { Span.fieldModifiedEvents | Span.fieldModifiedLinks | Span.fieldModifiedStatus | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -342,7 +350,7 @@ public void encode(Span val) throws IOException { if ((fieldMask & Span.fieldModifiedAttributes) != 0) { // Encode Attributes - this.attributesEncoder.encode(val.attributes); + this.attributesEncoder.encode(val.attributes, forceFields); } if ((fieldMask & Span.fieldModifiedDroppedAttributesCount) != 0) { @@ -352,17 +360,17 @@ public void encode(Span val) throws IOException { if ((fieldMask & Span.fieldModifiedEvents) != 0) { // Encode Events - this.eventsEncoder.encode(val.events); + this.eventsEncoder.encode(val.events, forceFields); } if ((fieldMask & Span.fieldModifiedLinks) != 0) { // Encode Links - this.linksEncoder.encode(val.links); + this.linksEncoder.encode(val.links, forceFields); } if ((fieldMask & Span.fieldModifiedStatus) != 0) { // Encode Status - this.statusEncoder.encode(val.status); + this.statusEncoder.encode(val.status, forceFields); } // Account written bits in the limiter. @@ -497,4 +505,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/SpanStatus.java b/java/src/main/java/com/example/otelstef/SpanStatus.java index a5030b14..d6e0beca 100644 --- a/java/src/main/java/com/example/otelstef/SpanStatus.java +++ b/java/src/main/java/com/example/otelstef/SpanStatus.java @@ -113,8 +113,8 @@ void markUnmodifiedRecursively() { public SpanStatus clone() { SpanStatus cpy = new SpanStatus(); - cpy.message = this.message; - cpy.code = this.code; + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/SpanStatusEncoder.java b/java/src/main/java/com/example/otelstef/SpanStatusEncoder.java index 5a81b236..2bb51056 100644 --- a/java/src/main/java/com/example/otelstef/SpanStatusEncoder.java +++ b/java/src/main/java/com/example/otelstef/SpanStatusEncoder.java @@ -77,18 +77,26 @@ public void reset() { // encode encodes val into buf public void encode(SpanStatus val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(SpanStatus val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = SpanStatus.fieldModifiedMessage | SpanStatus.fieldModifiedCode | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -141,4 +149,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/Spans.java b/java/src/main/java/com/example/otelstef/Spans.java index ca82f1f9..feb98eb9 100644 --- a/java/src/main/java/com/example/otelstef/Spans.java +++ b/java/src/main/java/com/example/otelstef/Spans.java @@ -159,10 +159,8 @@ void markUnmodifiedRecursively() { public Spans clone() { Spans cpy = new Spans(); - cpy.envelope = this.envelope.clone(); - cpy.resource = this.resource.clone(); - cpy.scope = this.scope.clone(); - cpy.span = this.span.clone(); + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/SpansEncoder.java b/java/src/main/java/com/example/otelstef/SpansEncoder.java index bad31910..3fa3297b 100644 --- a/java/src/main/java/com/example/otelstef/SpansEncoder.java +++ b/java/src/main/java/com/example/otelstef/SpansEncoder.java @@ -141,20 +141,28 @@ public void reset() { // encode encodes val into buf public void encode(Spans val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(Spans val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = Spans.fieldModifiedEnvelope | Spans.fieldModifiedResource | Spans.fieldModifiedScope | Spans.fieldModifiedSpan | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -167,22 +175,22 @@ public void encode(Spans val) throws IOException { if ((fieldMask & Spans.fieldModifiedEnvelope) != 0) { // Encode Envelope - this.envelopeEncoder.encode(val.envelope); + this.envelopeEncoder.encode(val.envelope, forceFields); } if ((fieldMask & Spans.fieldModifiedResource) != 0) { // Encode Resource - this.resourceEncoder.encode(val.resource); + this.resourceEncoder.encode(val.resource, forceFields); } if ((fieldMask & Spans.fieldModifiedScope) != 0) { // Encode Scope - this.scopeEncoder.encode(val.scope); + this.scopeEncoder.encode(val.scope, forceFields); } if ((fieldMask & Spans.fieldModifiedSpan) != 0) { // Encode Span - this.spanEncoder.encode(val.span); + this.spanEncoder.encode(val.span, forceFields); } // Account written bits in the limiter. @@ -237,4 +245,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/SummaryValue.java b/java/src/main/java/com/example/otelstef/SummaryValue.java index 7a618506..d71f2b36 100644 --- a/java/src/main/java/com/example/otelstef/SummaryValue.java +++ b/java/src/main/java/com/example/otelstef/SummaryValue.java @@ -140,9 +140,8 @@ void markUnmodifiedRecursively() { public SummaryValue clone() { SummaryValue cpy = new SummaryValue(); - cpy.count = this.count; - cpy.sum = this.sum; - cpy.quantileValues = this.quantileValues.clone(); + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/java/src/main/java/com/example/otelstef/SummaryValueEncoder.java b/java/src/main/java/com/example/otelstef/SummaryValueEncoder.java index 5e3233db..212f8d19 100644 --- a/java/src/main/java/com/example/otelstef/SummaryValueEncoder.java +++ b/java/src/main/java/com/example/otelstef/SummaryValueEncoder.java @@ -99,19 +99,27 @@ public void reset() { // encode encodes val into buf public void encode(SummaryValue val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode(SummaryValue val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = SummaryValue.fieldModifiedCount | SummaryValue.fieldModifiedSum | SummaryValue.fieldModifiedQuantileValues | 0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -134,7 +142,7 @@ public void encode(SummaryValue val) throws IOException { if ((fieldMask & SummaryValue.fieldModifiedQuantileValues) != 0) { // Encode QuantileValues - this.quantileValuesEncoder.encode(val.quantileValues); + this.quantileValuesEncoder.encode(val.quantileValues, forceFields); } // Account written bits in the limiter. @@ -178,4 +186,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } - diff --git a/java/src/main/java/com/example/otelstef/Uint64ArrayEncoder.java b/java/src/main/java/com/example/otelstef/Uint64ArrayEncoder.java index b859e147..c7cfd943 100644 --- a/java/src/main/java/com/example/otelstef/Uint64ArrayEncoder.java +++ b/java/src/main/java/com/example/otelstef/Uint64ArrayEncoder.java @@ -33,12 +33,15 @@ public void reset() { } public void encode(Uint64Array arr) throws IOException { + encode(arr, false); + } + + public void encode(Uint64Array arr, boolean forceAllElements) throws IOException { int oldBitLen = buf.bitCount(); // Write the length of the array. int newLen = arr.elemsLen; buf.writeUvarintCompact(newLen); - for (int i = 0; i < newLen; i++) { elemEncoder.encode(arr.elems[i]); } @@ -55,4 +58,3 @@ public void collectColumns(WriteColumnSet columnSet) { } } } - diff --git a/java/src/main/java/com/example/otelstef/WriterState.java b/java/src/main/java/com/example/otelstef/WriterState.java index f54fbf2e..01bb20e9 100644 --- a/java/src/main/java/com/example/otelstef/WriterState.java +++ b/java/src/main/java/com/example/otelstef/WriterState.java @@ -14,6 +14,8 @@ public class WriterState { // Dictionaries final StringDictEncoderDict AnyValueString; final StringDictEncoderDict AttributeKey; + final StringDictEncoderDict LogEventName; + final StringDictEncoderDict LogSeverityText; final MetricEncoderDict Metric; final StringDictEncoderDict MetricDescription; final StringDictEncoderDict MetricName; @@ -45,6 +47,8 @@ public class WriterState { KeyValueListEncoder KeyValueListEncoder; LinkEncoder LinkEncoder; LinkArrayEncoder LinkArrayEncoder; + LogRecordEncoder LogRecordEncoder; + LogsEncoder LogsEncoder; MetricEncoder MetricEncoder; MetricsEncoder MetricsEncoder; PointEncoder PointEncoder; @@ -64,6 +68,8 @@ public WriterState() { limiter = new SizeLimiter(); AnyValueString = new StringDictEncoderDict(); AttributeKey = new StringDictEncoderDict(); + LogEventName = new StringDictEncoderDict(); + LogSeverityText = new StringDictEncoderDict(); Metric = new MetricEncoderDict(); MetricDescription = new StringDictEncoderDict(); MetricName = new StringDictEncoderDict(); @@ -85,6 +91,8 @@ public void init(WriterOptions opts) { // Init dictionaries AnyValueString.init(limiter); AttributeKey.init(limiter); + LogEventName.init(limiter); + LogSeverityText.init(limiter); Metric.init(limiter); MetricDescription.init(limiter); MetricName.init(limiter); @@ -103,6 +111,8 @@ public void resetDicts() { limiter.resetDict(); AnyValueString.reset(); AttributeKey.reset(); + LogEventName.reset(); + LogSeverityText.reset(); Metric.reset(); MetricDescription.reset(); MetricName.reset(); diff --git a/java/src/test/java/tests/CloneTest.java b/java/src/test/java/tests/CloneTest.java new file mode 100644 index 00000000..5ed3bbc0 --- /dev/null +++ b/java/src/test/java/tests/CloneTest.java @@ -0,0 +1,124 @@ +package tests; + +import com.example.otelstef.AnyValue; +import com.example.otelstef.Exemplar; +import com.example.otelstef.ExemplarValue; +import com.example.otelstef.LogRecord; +import com.example.otelstef.Logs; +import com.example.otelstef.Point; +import com.example.otelstef.PointValue; +import net.stef.StringValue; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.HashSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CloneTest { + @Test + void logsCloneTracksExistingAttributeValueModifications() { + Logs source = new Logs(); + AnyValue attributeValue = new LogRecord().getBody(); + attributeValue.setInt64(1); + source.getLog().getAttributes().append(new StringValue("key"), attributeValue); + + Logs copy = source.clone(); + + assertFalse(copy.isLogModified()); + assertFalse(copy.getLog().isAttributesModified()); + + elementValue(copy.getLog().getAttributes().at(0)).setInt64(2); + + assertTrue(copy.isLogModified()); + assertTrue(copy.getLog().isAttributesModified()); + } + + @Test + void logsCloneTracksExistingNestedBodyMapValueModifications() { + Logs source = new Logs(); + source.getLog().getBody().setType(AnyValue.Type.TypeKVList); + AnyValue mapValue = new LogRecord().getBody(); + mapValue.setInt64(1); + source.getLog().getBody().getKVList().append(new StringValue("key"), mapValue); + + Logs copy = source.clone(); + + assertFalse(copy.isLogModified()); + assertFalse(copy.getLog().isBodyModified()); + + elementValue(copy.getLog().getBody().getKVList().at(0)).setInt64(2); + + assertTrue(copy.isLogModified()); + assertTrue(copy.getLog().isBodyModified()); + } + + @Test + void logsCloneTracksNestedLogModifications() { + Logs source = new Logs(); + source.getLog().setTimeUnixNano(1); + + Logs copy = source.clone(); + + assertFalse(copy.isLogModified()); + assertFalse(copy.getLog().isTimeUnixNanoModified()); + + copy.getLog().setTimeUnixNano(2); + + assertTrue(copy.isLogModified()); + assertTrue(copy.getLog().isTimeUnixNanoModified()); + } + + @Test + void logRecordCloneTracksNestedBodyModifications() { + LogRecord source = new LogRecord(); + source.getBody().setInt64(1); + + LogRecord copy = source.clone(); + + assertFalse(copy.isBodyModified()); + + copy.getBody().setInt64(2); + + assertTrue(copy.isBodyModified()); + } + + @Test + void oneofClonePreservesEqualityAndHashCodeAfterVariantSwitch() { + AnyValue anyValue = new LogRecord().getBody(); + anyValue.setInt64(42); + anyValue.setFloat64(1.0); + assertCloneHasEqualHashCode(anyValue, anyValue.clone()); + + PointValue pointValue = new Point().getValue(); + pointValue.setInt64(42); + pointValue.setFloat64(1.0); + assertCloneHasEqualHashCode(pointValue, pointValue.clone()); + + ExemplarValue exemplarValue = new Exemplar().getValue(); + exemplarValue.setInt64(42); + exemplarValue.setFloat64(1.0); + assertCloneHasEqualHashCode(exemplarValue, exemplarValue.clone()); + } + + private static void assertCloneHasEqualHashCode(T source, T copy) { + assertEquals(source, copy); + assertEquals(source.hashCode(), copy.hashCode()); + + HashSet values = new HashSet<>(); + values.add(source); + assertTrue(values.contains(copy)); + } + + private static AnyValue elementValue(Object elem) { + try { + Field valueField = elem.getClass().getDeclaredField("value"); + valueField.setAccessible(true); + return (AnyValue) valueField.get(elem); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } +} diff --git a/java/src/test/java/tests/EncoderResetTest.java b/java/src/test/java/tests/EncoderResetTest.java new file mode 100644 index 00000000..0a77334c --- /dev/null +++ b/java/src/test/java/tests/EncoderResetTest.java @@ -0,0 +1,118 @@ +package tests; + +import com.example.otelstef.AnyValue; +import com.example.otelstef.LogsWriter; +import com.example.otelstef.Metrics; +import com.example.otelstef.MetricsReader; +import com.example.otelstef.MetricsWriter; +import com.example.otelstef.PointValue; +import com.example.otelstef.QuantileValueArray; +import net.stef.ChunkWriter; +import net.stef.FrameFlags; +import net.stef.MemChunkWriter; +import net.stef.ReadOptions; +import net.stef.ReadResult; +import net.stef.StringValue; +import net.stef.WriterOptions; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class EncoderResetTest { + private static AnyValue newAnyValue() throws Exception { + Constructor constructor = AnyValue.class.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } + + private static class CapturingChunkWriter implements ChunkWriter { + private final List chunks = new ArrayList<>(); + + @Override + public void writeChunk(byte[] header, byte[] content) throws IOException { + ByteArrayOutputStream chunk = new ByteArrayOutputStream(); + chunk.write(header); + if (content != null) { + chunk.write(content); + } + chunks.add(chunk.toByteArray()); + } + + byte[] streamWithLastDataFrame() throws IOException { + // A standalone stream consists of the fixed header, variable-header frame, + // and one independently decodable data frame. + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + stream.write(chunks.get(0)); + stream.write(chunks.get(1)); + stream.write(chunks.get(chunks.size() - 1)); + return stream.toByteArray(); + } + } + + @Test + void forceModifiedFieldsIsConsumedByNextWrite() throws Exception { + LogsWriter writer = new LogsWriter(new MemChunkWriter(), WriterOptions.builder().build()); + + Field encoderField = LogsWriter.class.getDeclaredField("encoder"); + encoderField.setAccessible(true); + Object encoder = encoderField.get(writer); + + Method reset = encoder.getClass().getDeclaredMethod("reset"); + reset.setAccessible(true); + reset.invoke(encoder); + + writer.write(); + + Field forceModifiedFields = encoder.getClass().getDeclaredField("forceModifiedFields"); + forceModifiedFields.setAccessible(true); + assertFalse(forceModifiedFields.getBoolean(encoder)); + } + + @Test + void restartedFrameFullyEncodesEveryCollectionElement() throws Exception { + CapturingChunkWriter output = new CapturingChunkWriter(); + int restartFlags = FrameFlags.RestartDictionaries | FrameFlags.RestartCodecs; + MetricsWriter writer = new MetricsWriter( + output, + WriterOptions.builder().frameRestartFlags(restartFlags).build() + ); + + writer.record.getPoint().getValue().setType(PointValue.Type.TypeSummary); + QuantileValueArray quantiles = writer.record.getPoint().getValue().getSummary().getQuantileValues(); + quantiles.ensureLen(2); + quantiles.at(0).setQuantile(0.5); + quantiles.at(0).setValue(10.0); + quantiles.at(1).setQuantile(0.99); + quantiles.at(1).setValue(20.0); + + AnyValue firstValue = newAnyValue(); + firstValue.setInt64(1); + writer.record.getAttributes().append(new StringValue("first"), firstValue); + AnyValue secondValue = newAnyValue(); + secondValue.setInt64(2); + writer.record.getAttributes().append(new StringValue("second"), secondValue); + + Metrics expected = writer.record.clone(); + writer.write(); + // Write the unchanged record again. Its modification masks are now clear, + // but the restarted frame must still contain a complete representation. + writer.write(); + + MetricsReader reader = new MetricsReader( + new ByteArrayInputStream(output.streamWithLastDataFrame()) + ); + assertEquals(ReadResult.Success, reader.read(ReadOptions.none)); + assertTrue(reader.record.equals(expected)); + } +} diff --git a/stefc/templates/go/array.go.tmpl b/stefc/templates/go/array.go.tmpl index b79e3ce5..d36aeb09 100644 --- a/stefc/templates/go/array.go.tmpl +++ b/stefc/templates/go/array.go.tmpl @@ -55,6 +55,23 @@ func (e *{{.ArrayName}}) fixParent(parentModifiedFields *modifiedFields) { e.parentModifiedFields = parentModifiedFields } +// attachParent establishes both parent pointer and bit in a newly copied array. +func (e *{{.ArrayName}}) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + e.parentModifiedFields = parentModifiedFields + e.parentModifiedBit = parentModifiedBit + {{- if not .ElemType.IsPrimitive}} + for i := range e.elems { + {{- if .ElemType.DictName}} + if !e.elems[i].canBeShared() { + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + } + {{- else}} + e.elems[i].attachParent(parentModifiedFields, parentModifiedBit) + {{- end}} + } + {{- end}} +} + func (e *{{.ArrayName}}) canBeShared() bool { // An array can never be shared. return false @@ -137,6 +154,21 @@ func (e* {{.ArrayName}}) setUnmodifiedRecursively() { {{end}} } +// clearModifiedRecursively clears modification state in all mutable elements. +func (e* {{.ArrayName}}) clearModifiedRecursively() { +{{if not .ElemType.IsPrimitive -}} + for i:=0; i < len(e.elems); i++ { + {{- if .ElemType.DictName}} + if !e.elems[i].canBeShared() { + e.elems[i].clearModifiedRecursively() + } + {{- else}} + e.elems[i].clearModifiedRecursively() + {{- end}} + } +{{end}} +} + // computeDiff compares e and val and returns true if they differ. // All fields that are different in e will be marked as modified. func (e *{{ .ArrayName }}) computeDiff(val *{{ .ArrayName }}) (ret bool) { @@ -384,7 +416,7 @@ func (a *{{ .ArrayName }}) mutateRandom(random *rand.Rand, schem *schema.Schema, {{- if .ElemType.DictName}} if a.elems[i].canBeShared() { // Elem may be shared by pointer. Clone it to have exclusive ownership. - a.elems[i] = a.elems[i].Clone(&Allocators{}) + a.elems[i] = a.elems[i].clone(&Allocators{}) } {{- end}} a.elems[i].mutateRandom(random, schem, limiter) @@ -579,4 +611,4 @@ func (d *{{ .ArrayName }}Decoder) Decode(dst *{{.ArrayName}}) error { } return d.buf.Error() -} \ No newline at end of file +} diff --git a/stefc/templates/go/modifiedfields.go.tmpl b/stefc/templates/go/modifiedfields.go.tmpl index 0bcdaa3e..2821cfd0 100644 --- a/stefc/templates/go/modifiedfields.go.tmpl +++ b/stefc/templates/go/modifiedfields.go.tmpl @@ -114,6 +114,13 @@ func (m *modifiedFieldsMultimap) fixParent(parentModifiedFields *modifiedFields) m.vals.parent = parentModifiedFields } +func (m *modifiedFieldsMultimap) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.keys.parent = parentModifiedFields + m.vals.parent = parentModifiedFields + m.keys.parentBit = parentModifiedBit + m.vals.parentBit = parentModifiedBit +} + // Returns a bitmask to be set in keys or vals to mark modification of // multimap element at index. func (m* modifiedFieldsMultimap) maskForIndex(index int) uint64 { diff --git a/stefc/templates/go/multimap.go.tmpl b/stefc/templates/go/multimap.go.tmpl index 6f0ecaee..87cd24c7 100644 --- a/stefc/templates/go/multimap.go.tmpl +++ b/stefc/templates/go/multimap.go.tmpl @@ -70,6 +70,20 @@ func (m *{{.MultimapName}}) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointers and bits in a newly copied multimap. +func (m *{{.MultimapName}}) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + m.modifiedElems.attachParent(parentModifiedFields, parentModifiedBit) + elems := m.elems[:m.initedCount] + for i:=0; i < len(elems); i++ { + {{- if not .Key.Type.IsPrimitive}} + elems[i].key.attachParent(&m.modifiedElems.keys, m.modifiedElems.maskForIndex(i)) + {{- end}} + {{- if not .Value.Type.IsPrimitive}} + elems[i].value.attachParent(&m.modifiedElems.vals, m.modifiedElems.maskForIndex(i)) + {{- end}} + } +} + func (m *{{.MultimapName}}) canBeShared() bool { // Multimap can never be shared. return false @@ -159,6 +173,19 @@ func (m* {{.MultimapName}}) setUnmodifiedRecursively() { m.modifiedElems.setUnmodifiedAll() } +// clearModifiedRecursively clears modification state in all mutable elements. +func (m* {{.MultimapName}}) clearModifiedRecursively() { + for i:=0; i < len(m.elems); i++ { + {{- if not .Key.Type.IsPrimitive}} + m.elems[i].key.clearModifiedRecursively() + {{- end}} + {{- if not .Value.Type.IsPrimitive}} + m.elems[i].value.clearModifiedRecursively() + {{- end}} + } + m.modifiedElems.setUnmodifiedAll() +} + // computeDiff compares m to val and returns true if they differ. // All fields that are different in m will be marked as modified. func (m *{{.MultimapName}}) computeDiff(val *{{.MultimapName}}) (ret bool) { @@ -236,7 +263,7 @@ func (m *{{.MultimapName}}) SetKey(i int, k {{if .Key.Type.Flags.PassByPtr}}*{{e } else { if !m.elems[i].key.IsEqual(k) { if m.elems[i].key.canBeShared() { - m.elems[i].key = m.elems[i].key.Clone(&Allocators{}) + m.elems[i].key = m.elems[i].key.clone(&Allocators{}) } m.elems[i].key.CopyFrom({{.Key.Type.ToStorage "k"}}) m.modifiedElems.markKeyModified(i) @@ -266,7 +293,7 @@ func (m *{{.MultimapName}}) SetValue(i int, v {{if.Value.Type.Flags.PassByPtr}}* } else { if !m.elems[i].value.IsEqual(v) { if m.elems[i].value.canBeShared() { - m.elems[i].value = m.elems[i].value.Clone(&Allocators{}) + m.elems[i].value = m.elems[i].value.clone(&Allocators{}) } m.elems[i].value.CopyFrom({{.Value.Type.ToStorage "v"}}) m.modifiedElems.markValModified(i) diff --git a/stefc/templates/go/oneof.go.tmpl b/stefc/templates/go/oneof.go.tmpl index 86afa5a1..e0d22298 100644 --- a/stefc/templates/go/oneof.go.tmpl +++ b/stefc/templates/go/oneof.go.tmpl @@ -114,6 +114,27 @@ func (s *{{ $.StructName }}) fixParent(parentModifiedFields *modifiedFields) { } } +// attachParent establishes both parent pointer and bit in a newly copied oneof. +func (s *{{ $.StructName }}) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.parentModifiedFields = parentModifiedFields + s.parentModifiedBit = parentModifiedBit + + switch s.Type() { + {{- range .Fields }} + {{- if not .Type.IsPrimitive }} + case {{ $.StructName }}Type{{.Name}}: + {{- if .Type.DictName}} + if !s.{{.name}}Ptr().canBeShared() { + s.{{.name}}Ptr().attachParent(parentModifiedFields, parentModifiedBit) + } + {{- else}} + s.{{.name}}Ptr().attachParent(parentModifiedFields, parentModifiedBit) + {{- end}} + {{- end }} + {{- end }} + } +} + type {{$.StructName }}Type byte const ( {{.StructName }}TypeNone = {{$.StructName }}Type(iota) @@ -238,39 +259,46 @@ func (s *{{ .StructName }}) canBeShared() bool { return false } -func (s *{{ .StructName }}) cloneShared(allocators *Allocators) {{if .Type.Flags.StoreByPtr}}*{{end}}{{.StructName}} { - // Oneof is not shareable, so cloneShared is just a Clone. - return s.Clone(allocators) -} - -func (s *{{ .StructName }}) Clone(allocators *Allocators) {{if .Type.Flags.StoreByPtr}}*{{end}}{{.StructName}} { -{{- if .Type.Flags.StoreByPtr}} +{{if .Type.Flags.StoreByPtr}} +func (s *{{ .StructName }}) clone(allocators *Allocators) *{{.StructName}} { allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof({{.StructName}}{}))) c := allocators.{{.StructName}}.Alloc() -{{- else}} - c := {{.StructName}}{} -{{- end}} - c.clearValSetType(s.Type()) + s.CloneTo(c, allocators) + return c +} + +func (s *{{ .StructName }}) cloneShared(allocators *Allocators) *{{.StructName}} { + // Oneof is not shareable, so cloneShared is just a clone. + return s.clone(allocators) +} +{{end}} + +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *{{ .StructName }}) CloneTo(dst *{{.StructName}}, allocators *Allocators) { + *dst = {{.StructName}}{} + dst.clearValSetType(s.Type()) switch s.Type() { {{- range .Fields }} {{- $isStringOrBytes := or (eq .Type.Storage "string") (eq .Type.Storage "pkg.Bytes") }} case {{ $.StructName }}Type{{.Name}}: {{- if .Type.IsPrimitive}} {{- if $isStringOrBytes}} - c.set{{.Name}}(s.{{.Name}}()) + dst.set{{.Name}}(s.{{.Name}}()) {{- else}} - *c.{{.name}}Ptr() = *s.{{.name}}Ptr() + *dst.{{.name}}Ptr() = *s.{{.name}}Ptr() {{- end}} {{- else if .Type.Flags.StoreByPtr}} - c.ptr = unsafe.Pointer(s.{{.name}}Ptr().cloneShared(allocators)) + dst.ptr = unsafe.Pointer(s.{{.name}}Ptr().cloneShared(allocators)) {{- else}} allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof({{.Type.Storage}}{}))) - c.alloc{{.Name}}Alloc(allocators) - copyToNew{{.Type.Storage}}(c.{{.name}}Ptr(), s.{{.name}}Ptr(), allocators) + dst.alloc{{.Name}}Alloc(allocators) + copyToNew{{.Type.Storage}}(dst.{{.name}}Ptr(), s.{{.name}}Ptr(), allocators) {{- end}} {{- end }} } - return c + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -376,6 +404,24 @@ func (s *{{ $.StructName }}) setUnmodifiedRecursively() { } } +// clearModifiedRecursively clears modification state in all mutable descendants. +func (s *{{ $.StructName }}) clearModifiedRecursively() { + switch s.Type() { + {{- range .Fields}} + {{- if not .Type.IsPrimitive}} + case {{ $.StructName }}Type{{.Name}}: + {{- if .Type.DictName}} + if !s.{{.name}}Ptr().canBeShared() { + s.{{.name}}Ptr().clearModifiedRecursively() + } + {{- else}} + s.{{.name}}Ptr().clearModifiedRecursively() + {{- end}} + {{- end}} + {{- end}} + } +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *{{ $.StructName }}) computeDiff(val *{{ $.StructName }}) (ret bool) { diff --git a/stefc/templates/go/struct.go.tmpl b/stefc/templates/go/struct.go.tmpl index 27a60cf6..2fef13bd 100644 --- a/stefc/templates/go/struct.go.tmpl +++ b/stefc/templates/go/struct.go.tmpl @@ -169,6 +169,39 @@ func (s *{{ $.StructName }}) fixParent(parentModifiedFields *modifiedFields) { {{- end -}} } +// attachParent establishes both parent pointers and bits in a newly copied struct. +func (s *{{ $.StructName }}) attachParent(parentModifiedFields *modifiedFields, parentModifiedBit uint64) { + s.modifiedFields.parent = parentModifiedFields + s.modifiedFields.parentBit = parentModifiedBit + {{- range .Fields }} + {{- if not .Type.IsPrimitive }} + {{- if .Type.Flags.StoreByPtr}} + {{- if .Optional}} + if s.Has{{.Name}}() && s.{{.name}} != nil { + {{- if .Type.DictName}} + if !s.{{.name}}.canBeShared() { + s.{{.name}}.attachParent(&s.modifiedFields, fieldModified{{ $.StructName }}{{.Name}}) + } + {{- else}} + s.{{.name}}.attachParent(&s.modifiedFields, fieldModified{{ $.StructName }}{{.Name}}) + {{- end}} + } + {{- else}} + {{- if .Type.DictName}} + if !s.{{.name}}.canBeShared() { + s.{{.name}}.attachParent(&s.modifiedFields, fieldModified{{ $.StructName }}{{.Name}}) + } + {{- else}} + s.{{.name}}.attachParent(&s.modifiedFields, fieldModified{{ $.StructName }}{{.Name}}) + {{- end}} + {{- end}} + {{- else }} + s.{{.name}}.attachParent(&s.modifiedFields, fieldModified{{ $.StructName }}{{.Name}}) + {{- end }} + {{- end -}} + {{- end -}} +} + {{if .DictName}} // Freeze the struct. Any attempt to modify it after this will panic. // This marks the struct as eligible for safely sharing by pointer without cloning, @@ -242,7 +275,7 @@ func (s *{{ $.StructName }}) Set{{.Name}}(v {{if .Type.Flags.PassByPtr}}*{{end}} } } else { if s.{{.name}}.canBeShared() { - s.{{.name}} = s.{{.name}}.Clone(&Allocators{}) + s.{{.name}} = s.{{.name}}.clone(&Allocators{}) } s.{{.name}}.CopyFrom({{.Type.ToStorage "v"}}) s.modifiedFields.markModified(fieldModified{{ $.StructName }}{{.Name}}) @@ -321,6 +354,29 @@ func (s *{{ $.StructName }}) setUnmodifiedRecursively() { s.modifiedFields.mask = 0 } +// clearModifiedRecursively clears modification state in s and all mutable descendants. +// Unlike setUnmodifiedRecursively it does not rely on parent modification bits being set. +func (s *{{ $.StructName }}) clearModifiedRecursively() { + {{- range .Fields }} + {{- if not .Type.IsPrimitive}} + {{- if .Optional}} + if s.Has{{.Name}}() { + {{- end}} + {{- if .Type.DictName}} + if !s.{{.name}}.canBeShared() { + s.{{.name}}.clearModifiedRecursively() + } + {{- else}} + s.{{.name}}.clearModifiedRecursively() + {{- end}} + {{- if .Optional}} + } + {{- end}} + {{- end}} + {{- end }} + s.modifiedFields.mask = 0 +} + // computeDiff compares s and val and returns true if they differ. // All fields that are different in s will be marked as modified. func (s *{{ $.StructName }}) computeDiff(val *{{ $.StructName }}) (ret bool) { @@ -368,40 +424,58 @@ func (s *{{ .StructName }}) canBeShared() bool { return {{if .DictName}}s.isFrozen(){{else}}false{{end}} } +{{if .Type.Flags.StoreByPtr}} +// clone returns a clone of s allocated using allocators. +func (s *{{ .StructName }}) clone(allocators *Allocators) *{{.StructName}} { + allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof({{.StructName}}{}))) + c := allocators.{{.StructName}}.Alloc() + s.CloneTo(c, allocators) + return c +} + // cloneShared returns a clone of s. It may return s if it is safe to share without cloning // (for example if s is frozen). -func (s *{{ .StructName }}) cloneShared(allocators *Allocators) {{if .Type.Flags.StoreByPtr}}*{{end}}{{.StructName}} { +func (s *{{ .StructName }}) cloneShared(allocators *Allocators) *{{.StructName}} { {{- if .DictName}} if s.isFrozen() { // If s is frozen it means it is safe to share without cloning. return s } {{- end}} - return s.Clone(allocators) + return s.clone(allocators) } +{{end}} -func (s *{{ .StructName }}) Clone(allocators *Allocators) {{if .Type.Flags.StoreByPtr}}*{{end}}{{.StructName}} { -{{- if .Type.Flags.StoreByPtr}} - allocators.allocSizeChecker.AddAllocSize(uint(unsafe.Sizeof({{.StructName}}{}))) - c := allocators.{{.StructName}}.Alloc() - *c = {{.StructName}}{ -{{- else}} - c := {{.StructName}}{ -{{- end}} +// CloneTo performs a deep copy from s to dst. dst does not need to be initialized +// and must not alias s. dst must remain at a stable address while it is modified. +func (s *{{ .StructName }}) CloneTo(dst *{{.StructName}}, allocators *Allocators) { + *dst = {{.StructName}}{ {{- range .Fields }} {{- if .Type.IsPrimitive}} {{.name}}: s.{{.name}}, - {{- else if .Type.Flags.StoreByPtr}} - {{.name}}: s.{{.name}}.cloneShared(allocators), {{- end}} {{- end }} } {{- range .Fields }} - {{- if and (not .Type.IsPrimitive) (not .Type.Flags.StoreByPtr)}} - copyToNew{{.Type.Storage}}({{if .Type.Flags.TakePtr}}&{{end}}c.{{.name}}, {{if .Type.Flags.TakePtr}}&{{end}}s.{{.name}}, allocators) + {{- if not .Type.IsPrimitive}} + {{- if .Optional}} + if s.Has{{.Name}}() { + {{- end}} + {{- if .Type.Flags.StoreByPtr}} + dst.{{.name}} = s.{{.name}}.cloneShared(allocators) + {{- else}} + copyToNew{{.Type.Storage}}({{if .Type.Flags.TakePtr}}&{{end}}dst.{{.name}}, {{if .Type.Flags.TakePtr}}&{{end}}s.{{.name}}, allocators) + {{- end}} + {{- if .Optional}} + } + {{- end}} {{- end}} {{- end }} - return c + {{- if .OptionalFieldCount}} + dst.optionalFieldsPresent = s.optionalFieldsPresent + {{- end}} + dst.attachParent(nil, 0) + dst.clearModifiedRecursively() } // ByteSize returns approximate memory usage in bytes. Used to calculate @@ -581,12 +655,12 @@ func (s *{{ .StructName }}) mutateRandom(random *rand.Rand, schem *schema.Schema s.{{.name}} = new({{ .Type.Storage }}) s.{{.name}}.init(&s.modifiedFields, fieldModified{{ $.StructName }}{{.Name}}) } else { - s.{{.name}} = s.{{.name}}.Clone(&Allocators{}) + s.{{.name}} = s.{{.name}}.clone(&Allocators{}) } } if s.{{.name}}.canBeShared() { // {{.name}} may be shared by pointer. Clone it to have exclusive ownership. - s.{{.name}} = s.{{.name}}.Clone(&Allocators{}) + s.{{.name}} = s.{{.name}}.clone(&Allocators{}) } {{end}} s.{{.name}}.mutateRandom(random, schem, limiter) @@ -726,7 +800,7 @@ func (d *{{ .StructName }}EncoderDict) Add(val *{{ .StructName }}) { d.dict.Set(val, refNum) return } - clone := val.Clone(d.allocators) // Clone before adding to dictionary. + clone := val.clone(d.allocators) // Clone before adding to dictionary. clone.Freeze() // Freeze the clone so that it can be safely shared by pointer. d.dict.Set(clone, refNum) } @@ -1036,7 +1110,7 @@ func (d *{{ .StructName }}Decoder) Decode(dstPtr {{if.DictName}}*{{end}}*{{.Stru // *dstPtr is pointing to a element in the dictionary. We are not allowed // to modify it. Make a clone of it and decode into the clone. - val := (*dstPtr).Clone(d.allocators) + val := (*dstPtr).clone(d.allocators) if d.allocators.allocSizeChecker.IsOverLimit() { return pkg.ErrRecordAllocLimitExceeded } @@ -1184,4 +1258,4 @@ func {{.StructName}}WireSchema() (schema.WireSchema, error) { } return w,nil } -{{end}} \ No newline at end of file +{{end}} diff --git a/stefc/templates/java/arrayEncoder.java.tmpl b/stefc/templates/java/arrayEncoder.java.tmpl index 96d2f562..83a8cb3b 100644 --- a/stefc/templates/java/arrayEncoder.java.tmpl +++ b/stefc/templates/java/arrayEncoder.java.tmpl @@ -16,6 +16,10 @@ class {{ .ArrayName }}Encoder { private {{.ElemType.EncoderType}}Encoder elemEncoder; private WriterState state; private boolean isRecursive = false; + {{- if not .ElemType.IsPrimitive}} + // Set after reset so every element in the first non-empty array is encoded in full. + private boolean forceElements; + {{- end}} public void init(WriterState state, WriteColumnSet columns) throws IOException { this.state = state; @@ -47,21 +51,36 @@ class {{ .ArrayName }}Encoder { } public void reset() { + {{- if not .ElemType.IsPrimitive}} + forceElements = true; + {{- end}} if (!isRecursive) { elemEncoder.reset(); } } public void encode({{ .ArrayName }} arr) throws IOException { + encode(arr, false); + } + + public void encode({{ .ArrayName }} arr, boolean forceAllElements) throws IOException { int oldBitLen = buf.bitCount(); // Write the length of the array. int newLen = arr.elemsLen; buf.writeUvarintCompact(newLen); + {{- if not .ElemType.IsPrimitive}} + boolean forceElementsNow = forceAllElements || forceElements; + {{- end}} for (int i = 0; i < newLen; i++) { - elemEncoder.encode(arr.elems[i]); + elemEncoder.encode(arr.elems[i]{{if not .ElemType.IsPrimitive}}, forceElementsNow{{end}}); } + {{- if not .ElemType.IsPrimitive}} + if (newLen != 0) { + forceElements = false; + } + {{- end}} // Account written bits in the limiter. int newBitLen = buf.bitCount(); @@ -75,4 +94,3 @@ class {{ .ArrayName }}Encoder { } } } - diff --git a/stefc/templates/java/multimap.java.tmpl b/stefc/templates/java/multimap.java.tmpl index 24ebb7ec..a1d5eac5 100644 --- a/stefc/templates/java/multimap.java.tmpl +++ b/stefc/templates/java/multimap.java.tmpl @@ -158,6 +158,7 @@ public class {{ .MultimapName }} { elems[i].value.markUnmodifiedRecursively(); {{- end}} } + modifiedElems.markUnmodifiedAll(); } // Append adds a key-value pair to the multimap. diff --git a/stefc/templates/java/multimapEncoder.java.tmpl b/stefc/templates/java/multimapEncoder.java.tmpl index 7b304706..7e695a44 100644 --- a/stefc/templates/java/multimapEncoder.java.tmpl +++ b/stefc/templates/java/multimapEncoder.java.tmpl @@ -18,6 +18,8 @@ class {{ .MultimapName }}Encoder { private {{.Value.Type.EncoderType}}Encoder valueEncoder; private boolean isKeyRecursive = false; private boolean isValueRecursive = false; + // Set after reset so the first non-empty multimap is encoded in full. + private boolean forceFull; public void init(WriterState state, WriteColumnSet columns) throws IOException { // Remember this encoder in the state so that we can detect recursion. @@ -60,6 +62,7 @@ class {{ .MultimapName }}Encoder { } public void reset() { + forceFull = true; if (!isKeyRecursive) { keyEncoder.reset(); } @@ -69,6 +72,10 @@ class {{ .MultimapName }}Encoder { } public void encode({{ .MultimapName }} list) throws IOException { + encode(list, false); + } + + public void encode({{ .MultimapName }} list, boolean forceAllElements) throws IOException { int oldLen = buf.size(); if (list.elemsLen == 0) { @@ -80,11 +87,13 @@ class {{ .MultimapName }}Encoder { return; } - if (!list.areKeysModified() && list.elemsLen < 63) { + boolean encodeAllElements = forceAllElements || forceFull; + if (!encodeAllElements && !list.areKeysModified() && list.elemsLen < 63) { encodeValuesOnly(list); } else { - encodeFull(list); + encodeFull(list, encodeAllElements); } + forceFull = false; limiter.addFrameBytes(buf.size() - oldLen); @@ -115,14 +124,14 @@ class {{ .MultimapName }}Encoder { } } - private void encodeFull({{ .MultimapName }} list) throws IOException { + private void encodeFull({{ .MultimapName }} list, boolean forceAllElements) throws IOException { // Record multimap len (LSB is 1 to indicate full encoding). buf.writeUvarint(((long)list.elemsLen << 1) | 0b1); - // Encode keys and values. + // Encode keys and values. for (int i = 0; i < list.elemsLen; i++) { - keyEncoder.encode(list.elems[i].key); - valueEncoder.encode(list.elems[i].value); + keyEncoder.encode(list.elems[i].key{{if not .Key.Type.IsPrimitive}}, forceAllElements{{end}}); + valueEncoder.encode(list.elems[i].value{{if not .Value.Type.IsPrimitive}}, forceAllElements{{end}}); } } @@ -138,4 +147,3 @@ class {{ .MultimapName }}Encoder { } } } - diff --git a/stefc/templates/java/oneof.java.tmpl b/stefc/templates/java/oneof.java.tmpl index 287d5517..33355ec8 100644 --- a/stefc/templates/java/oneof.java.tmpl +++ b/stefc/templates/java/oneof.java.tmpl @@ -133,10 +133,8 @@ public class {{ .StructName }} { // Clone returns a deep copy of this oneof. public {{.StructName}} clone() { {{.StructName}} cpy = new {{.StructName}}(); - cpy.typ = this.typ; - {{- range .Fields }} - cpy.{{.name}} = this.{{.name}}{{if .Type.MustClone}}.clone(){{end}}; - {{- end }} + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } @@ -290,11 +288,13 @@ public class {{ .StructName }} { @Override public int hashCode() { - return Objects.hash( - {{- range $index, $element := .Fields }} - {{.name}}, + switch (this.typ) { + {{- range .Fields }} + case Type{{.Name}}: + return Objects.hash(typ, {{.name}}); {{- end }} - typ - ); + default: + return Objects.hash(typ); + } } } diff --git a/stefc/templates/java/oneofEncoder.java.tmpl b/stefc/templates/java/oneofEncoder.java.tmpl index a946de58..e58016af 100644 --- a/stefc/templates/java/oneofEncoder.java.tmpl +++ b/stefc/templates/java/oneofEncoder.java.tmpl @@ -79,6 +79,12 @@ class {{ .StructName }}Encoder { // Encode encodes val into buf public void encode({{ .StructName }} val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf and propagates forced full encoding through + // the selected non-primitive value. + public void encode({{ .StructName }} val, boolean forceAllFields) throws IOException { {{ $.StructName }}.Type typ = val.typ; if (typ.getValue() > fieldCount) { // The current field type is not supported in target schema. Encode the type as None. @@ -96,7 +102,7 @@ class {{ .StructName }}Encoder { {{- range .Fields }} case Type{{.Name}}: // Encode {{.Name}} - {{.name}}Encoder.encode(val.{{.name}}); + {{.name}}Encoder.encode(val.{{.name}}{{if not .IsPrimitive}}, forceAllFields{{end}}); break; {{- end }} } diff --git a/stefc/templates/java/struct.java.tmpl b/stefc/templates/java/struct.java.tmpl index 244f633a..1bf79cba 100644 --- a/stefc/templates/java/struct.java.tmpl +++ b/stefc/templates/java/struct.java.tmpl @@ -161,9 +161,8 @@ public class {{ .StructName }} { public {{.StructName}} clone() { {{.StructName}} cpy = new {{.StructName}}(); - {{- range .Fields }} - cpy.{{.name}} = this.{{.name}}{{if .Type.MustClone}}.clone(){{end}}; - {{- end }} + cpy.copyFrom(this); + cpy.markUnmodifiedRecursively(); return cpy; } diff --git a/stefc/templates/java/structEncoder.java.tmpl b/stefc/templates/java/structEncoder.java.tmpl index 09b73a19..50733e31 100644 --- a/stefc/templates/java/structEncoder.java.tmpl +++ b/stefc/templates/java/structEncoder.java.tmpl @@ -93,6 +93,12 @@ class {{ .StructName }}Encoder { // encode encodes val into buf public void encode({{ .StructName }} val) throws IOException { + encode(val, false); + } + + // encode encodes val into buf. forceAllFields is used by collection encoders + // when every element must be independently reconstructable after a restart. + public void encode({{ .StructName }} val, boolean forceAllFields) throws IOException { int oldLen = this.buf.bitCount(); {{if .DictName}} @@ -124,12 +130,14 @@ class {{ .StructName }}Encoder { // Mask that describes what fields are encoded. Start with all modified fields. long fieldMask = val.modifiedFields.mask; - // If forceModifiedFields we need to set to 1 all bits so that we + // On an encoder restart or when requested by a containing collection, // force writing of all fields. - if (this.forceModifiedFields) { + boolean forceFields = forceAllFields || this.forceModifiedFields; + if (forceFields) { fieldMask = {{- range .Fields }} {{ $.StructName }}.fieldModified{{.Name}} | {{end}}0L; + this.forceModifiedFields = false; } // Only write fields that we want to write. See init() for keepFieldMask. @@ -146,7 +154,7 @@ class {{ .StructName }}Encoder { if ((fieldMask & {{ $.StructName }}.fieldModified{{.Name}}) != 0 {{- if .Optional}} && (val.optionalFieldsPresent & {{ $.StructName }}.fieldPresent{{.Name}}) != 0{{end}}) { // Encode {{.Name}} - this.{{.name}}Encoder.encode(val.{{.name}}); + this.{{.name}}Encoder.encode(val.{{.name}}{{if not .IsPrimitive}}, forceFields{{end}}); } {{ end }} // Account written bits in the limiter. @@ -181,4 +189,3 @@ class {{ .StructName }}Encoder { {{- end }} } } -