diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e6a27b5c..9241c27d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## Pinealctx 1.2.0 (October 23, 2026) + +Synchronize upstream QuickFIX/Go v0.9.12 (`eff945dd`) while retaining the fork module path and tested parser boundary safeguards. + +### FEATURES +* Add `RepeatingGroup.Reset()` for explicit reuse of group instances. + +### BUG FIXES +* Avoid recursive read locking in `FieldMap.GetTime` when a writer is waiting. +* Forward variadic arguments correctly in the composite logger. +* Restore the upstream business reject reason for conditionally missing fields. +* Correct dictionary path diagnostics and remove a redundant parser buffer reset. +* Reject short/truncated messages, empty integer fields, and incomplete XML data without panicking. +* Include upstream nested-group regression coverage and preserve the fork's additional ancestor/sibling-group and malformed-input protections. + ## Pinealctx 1.1.2 (October 22, 2026) ### BUG FIXES @@ -31,6 +46,31 @@ tool, and unused conversion helpers. ### BUG FIXES * Honor `ValidateFieldsHaveValues=N` when a data dictionary is enabled. * Make the test suite portable across Windows filesystems, absent MongoDB integration settings, and equal clock ticks. +## 0.9.12 (September 30, 2026) + +### BUG FIXES +* Stop misattributing body fields that follow a nested repeating group [#756](https://github.com/quickfixgo/quickfix/pull/756) +* Forward OnEventf format arguments to the wrapped logs in the composite log [#779](https://github.com/quickfixgo/quickfix/pull/779) +* Correct the datadictionary parse error message in SessionFactory [#780](https://github.com/quickfixgo/quickfix/pull/780) + +## 0.9.11 (September 14, 2026) + +### FEATURES +* Adds `SetNewListenerCallback` to the Acceptor to allow custom `net.Listener` creation [#729](https://github.com/quickfixgo/quickfix/pull/729) +* Adds config option for ValidateFieldsHaveValues [#734](https://github.com/quickfixgo/quickfix/pull/734) +* Adds `Reset()` method to RepeatingGroup [#775](https://github.com/quickfixgo/quickfix/pull/775) + +### ENHANCEMENTS +* Make both initiator and acceptor fixIn channels buffered [#736](https://github.com/quickfixgo/quickfix/pull/736) +* Update devcontainer Go image to 1.25 [#748](https://github.com/quickfixgo/quickfix/pull/748) +* Remove redundant buffer Reset when reading messages [#777](https://github.com/quickfixgo/quickfix/pull/777) + +### BUG FIXES +* Resent messages with repeating groups on sessions without a data dictionary no longer produce an incorrect length and checksum [#743](https://github.com/quickfixgo/quickfix/pull/743) +* Validate each token of multi-value enum fields [#766](https://github.com/quickfixgo/quickfix/pull/766) +* Fix duplicate 10= checksum when resending a message ending in a repeating group [#767](https://github.com/quickfixgo/quickfix/pull/767) +* Resend Requests should not interleave with live traffic [#768](https://github.com/quickfixgo/quickfix/pull/768) +* Fix potential deadlock in FieldMap.GetTime [#776](https://github.com/quickfixgo/quickfix/pull/776) ## 0.9.10 (August 8, 2025) diff --git a/FORK.md b/FORK.md index 267e26293..7d395d9ef 100644 --- a/FORK.md +++ b/FORK.md @@ -19,6 +19,19 @@ Fork-specific changes should remain focused, documented, and covered by tests. Business-specific generators, logging adapters, credentials, and application wrappers belong in their consuming projects. +## Reviewed upstream baseline + +The fork includes upstream v0.9.12, commit `eff945dd59a41caeaed20f71916f08d37e0fda99`. +Upstream PR #756 fixes body fields after nested groups, a defect inherited from +upstream commit `6889cf10` (May 2024). The fork also resolves the ancestor before +classifying the next field, resets the path when switching root groups, and +returns errors for malformed/truncated groups, short messages, empty integer +fields, and truncated XML payloads. These differences are covered by +`message_group_boundary_test.go`; the unmodified v0.9.12 parser fails its nested +sibling-group and truncated-input cases. The remaining parser differences do not +alter session sequencing, transport writes, FileStore persistence, or resend +scheduling. + ## Synchronizing upstream The upstream remote is named `github.com` in the maintainer checkout. Fetch it diff --git a/errors.go b/errors.go index 34f1224ff..e9d120991 100644 --- a/errors.go +++ b/errors.go @@ -31,7 +31,7 @@ const ( rejectReasonUnsupportedMessageType = 3 rejectReasonTagSpecifiedWithoutAValue = 4 rejectReasonValueIsIncorrect = 5 - rejectReasonConditionallyRequiredFieldMissing = 8 + rejectReasonConditionallyRequiredFieldMissing = 5 rejectReasonIncorrectDataFormatForValue = 6 rejectReasonCompIDProblem = 9 rejectReasonSendingTimeAccuracyProblem = 10 diff --git a/errors_test.go b/errors_test.go index ac0eaca4a..c5b77d9b6 100644 --- a/errors_test.go +++ b/errors_test.go @@ -141,9 +141,7 @@ func TestValueIsIncorrect(t *testing.T) { func TestConditionallyRequiredFieldMissing(t *testing.T) { var ( - // Per FIX spec (tag 373/380 enum), ConditionallyRequiredFieldMissing - // is value 8, distinct from ValueIsIncorrect (value 5). See issue #721. - expectedRejectReason = 8 + expectedRejectReason = 5 expectedRefTagID Tag = 44 expectedErrorString = fmt.Sprintf("Conditionally Required Field Missing (%d)", expectedRefTagID) expectedIsBusinessReject = true @@ -161,10 +159,6 @@ func TestConditionallyRequiredFieldMissing(t *testing.T) { if msgRej.IsBusinessReject() != expectedIsBusinessReject { t.Error("Expected IsBusinessReject to be true\n") } - // Regression guard: the two reject reasons must not collide. - if msgRej.RejectReason() == ValueIsIncorrect(expectedRefTagID).RejectReason() { - t.Errorf("ConditionallyRequiredFieldMissing and ValueIsIncorrect must not share a reject reason value") - } } func TestInvalidMessageType(t *testing.T) { diff --git a/field_map.go b/field_map.go index 4aac64b1d..d982cdb3b 100644 --- a/field_map.go +++ b/field_map.go @@ -196,7 +196,7 @@ func (m FieldMap) GetTime(tag Tag) (t time.Time, err MessageRejectError) { m.rwLock.RLock() defer m.rwLock.RUnlock() - bytes, err := m.GetBytes(tag) + bytes, err := m.getBytesNoLock(tag) if err != nil { return } diff --git a/field_map_test.go b/field_map_test.go index 0e9078734..ac58342a4 100644 --- a/field_map_test.go +++ b/field_map_test.go @@ -17,7 +17,9 @@ package quickfix import ( "bytes" + "sync" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -202,3 +204,46 @@ func TestFieldMap_Remove(t *testing.T) { assert.False(t, fMap.Has(1)) assert.True(t, fMap.Has(2)) } + +// TestFieldMap_GetTimeConcurrentWithSet verifies the fix guarding against +// recursive read-locking in GetTime. +func TestFieldMap_GetTimeConcurrentWithSet(t *testing.T) { + var fMap FieldMap + fMap.init() + fMap.SetField(Tag(1), FIXUTCTimestamp{Time: time.Now()}) + fMap.SetField(Tag(2), FIXString("blah")) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 50000; i++ { + fMap.SetField(Tag(2), FIXString("blah")) + } + }() + + for r := 0; r < 8; r++ { + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 50000; i++ { + if _, err := fMap.GetTime(Tag(1)); err != nil { + t.Error(err) + return + } + } + }() + } + + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + + select { + case <-done: + case <-time.After(60 * time.Second): + t.Fatal("deadlock: GetTime did not complete concurrently with writers") + } +} diff --git a/fix_int.go b/fix_int.go index 6bd968003..a051313a4 100644 --- a/fix_int.go +++ b/fix_int.go @@ -31,7 +31,7 @@ const ( // atoi is similar to the function in strconv, but is tuned for ints appearing in FIX field types. func atoi(d []byte) (int, error) { - if d[0] == asciiMinus { + if len(d) > 0 && d[0] == asciiMinus { n, err := parseUInt(d[1:]) return (-1) * n, err } diff --git a/fix_int_test.go b/fix_int_test.go index caa9b7840..2aa4f9d0f 100644 --- a/fix_int_test.go +++ b/fix_int_test.go @@ -50,3 +50,9 @@ func BenchmarkFIXInt_Read(b *testing.B) { _ = field.Read(intBytes) } } + +func TestFIXInt_ReadEmpty(t *testing.T) { + field := FIXInt(7) + assert.NotPanics(t, func() { assert.Error(t, field.Read(nil)) }) + assert.Equal(t, FIXInt(7), field) +} diff --git a/log/composite/composite_log.go b/log/composite/composite_log.go index 034b1487c..3b5df37f7 100644 --- a/log/composite/composite_log.go +++ b/log/composite/composite_log.go @@ -43,7 +43,7 @@ func (l compositeLog) OnEvent(s string) { func (l compositeLog) OnEventf(format string, a ...interface{}) { for _, log := range l.logs { - log.OnEventf(format, a) + log.OnEventf(format, a...) } } diff --git a/log/composite/composite_log_test.go b/log/composite/composite_log_test.go index ab0669c2e..e7a2ac90f 100644 --- a/log/composite/composite_log_test.go +++ b/log/composite/composite_log_test.go @@ -107,3 +107,59 @@ func (suite *CompositeLogTestSuite) TearDownTest() { func TestCompositeLogTestSuite(t *testing.T) { suite.Run(t, new(CompositeLogTestSuite)) } + +// recordingLog records the events it is handed so a test can assert on what the +// composite log forwarded to its wrapped logs. +type recordingLog struct { + events []string +} + +func (r *recordingLog) OnIncoming([]byte) {} + +func (r *recordingLog) OnOutgoing([]byte) {} + +func (r *recordingLog) OnEvent(s string) { r.events = append(r.events, s) } + +func (r *recordingLog) OnEventf(format string, a ...interface{}) { + r.events = append(r.events, fmt.Sprintf(format, a...)) +} + +// recordingLogFactory hands out one pre-built recordingLog. +type recordingLogFactory struct { + log *recordingLog +} + +func (f recordingLogFactory) Create() (quickfix.Log, error) { return f.log, nil } + +func (f recordingLogFactory) CreateSessionLog(quickfix.SessionID) (quickfix.Log, error) { + return f.log, nil +} + +func TestCompositeLogOnEventfExpandsFormatArgs(t *testing.T) { + tests := []struct { + name string + format string + args []interface{} + expected string + }{ + {"single string arg", "session %s", []interface{}{"FIX.4.4"}, "session FIX.4.4"}, + {"mixed args", "session %s seq %d", []interface{}{"FIX.4.4", 7}, "session FIX.4.4 seq 7"}, + {"slice arg", "tags %v count %d", []interface{}{[]int{7, 9}, 2}, "tags [7 9] count 2"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + first, second := &recordingLog{}, &recordingLog{} + l, err := NewLogFactory([]quickfix.LogFactory{ + recordingLogFactory{first}, + recordingLogFactory{second}, + }).Create() + require.NoError(t, err) + + l.OnEventf(tt.format, tt.args...) + + require.Equal(t, []string{tt.expected}, first.events) + require.Equal(t, []string{tt.expected}, second.events) + }) + } +} diff --git a/message.go b/message.go index edd508327..2be6c0188 100644 --- a/message.go +++ b/message.go @@ -198,6 +198,10 @@ func doParsing(mp *msgParser) (err error) { if fieldCount == 0 { return parseError{OrigError: fmt.Sprintf("No Fields detected in %s", string(mp.rawBytes))} } + // A message needs the three leading header fields and a checksum. + if fieldCount < 4 { + return parseError{OrigError: "incomplete message header or trailer"} + } if cap(mp.msg.fields) < fieldCount { mp.msg.fields = make([]TagValue, fieldCount) } else { @@ -235,6 +239,9 @@ func doParsing(mp *msgParser) (err error) { mp.foundBody = false mp.foundTrailer = false for { + if mp.fieldIndex >= len(mp.msg.fields) { + return parseError{OrigError: "unterminated message"} + } mp.parsedFieldBytes = &mp.msg.fields[mp.fieldIndex] if xmlDataLen > 0 { mp.rawBytes, err = extractXMLDataField(mp.parsedFieldBytes, mp.rawBytes, xmlDataLen) @@ -561,6 +568,9 @@ func extractXMLDataField(parsedFieldBytes *TagValue, buffer []byte, dataLen int) remBytes = buffer return } + if dataLen < 0 || dataLen > len(buffer)-endIndex-2 { + return buffer, parseError{OrigError: "incomplete XML data field"} + } endIndex += dataLen + 1 err = parsedFieldBytes.parse(buffer[:endIndex+1]) diff --git a/message_group_boundary_test.go b/message_group_boundary_test.go index 4e3cefa03..441d97520 100644 --- a/message_group_boundary_test.go +++ b/message_group_boundary_test.go @@ -81,3 +81,52 @@ func TestNestedGroupUnterminatedInputReturnsError(t *testing.T) { }) } } + +func TestNestedGroupsResumeAtSiblingGroup(t *testing.T) { + dictionary, err := datadictionary.Parse("spec/FIX44.xml") + require.NoError(t, err) + for _, tc := range []struct{ name, prefix string }{ + {"sibling child after nested child", ""}, + {"new root after nested group", "555=1|600=leg|539=1|524=nested|804=1|545=leg-sub|"}, + } { + t.Run(tc.name, func(t *testing.T) { + wire := groupBoundaryWire("AE", tc.prefix+"552=1|54=1|37=order|453=1|448=party|447=D|452=3|802=1|523=sub|576=1|577=0|17=execution|") + message := NewMessage() + require.NoError(t, ParseMessageWithDataDictionary(message, bytes.NewBuffer(wire), dictionary, dictionary)) + sides := NewRepeatingGroup(552, GroupTemplate{GroupElement(54), GroupElement(37), NewRepeatingGroup(453, GroupTemplate{GroupElement(448), GroupElement(447), GroupElement(452), NewRepeatingGroup(802, GroupTemplate{GroupElement(523)})}), NewRepeatingGroup(576, GroupTemplate{GroupElement(577)})}) + require.NoError(t, message.Body.GetGroup(sides)) + require.Equal(t, 1, sides.Len()) + clearing := NewRepeatingGroup(576, GroupTemplate{GroupElement(577)}) + require.NoError(t, sides.Get(0).GetGroup(clearing)) + require.Equal(t, 1, clearing.Len()) + value, err := clearing.Get(0).GetString(577) + require.NoError(t, err) + require.Equal(t, "0", value) + require.False(t, message.Body.Has(577)) + execution, err := message.Body.GetString(17) + require.NoError(t, err) + require.Equal(t, "execution", execution) + reparsed := NewMessage() + require.NoError(t, ParseMessageWithDataDictionary(reparsed, bytes.NewBuffer(message.build()), dictionary, dictionary)) + sides.Reset() + require.NoError(t, reparsed.Body.GetGroup(sides)) + require.Equal(t, 1, sides.Len()) + }) + } +} + +func FuzzDictionaryGroupParsing(f *testing.F) { + dictionary, err := datadictionary.Parse("spec/FIX44.xml") + if err != nil { + f.Fatal(err) + } + f.Add([]byte("8=FIX.4.4\x019=20\x0135=n\x01212=999999\x01213=x\x0110=000\x01")) + f.Add(groupBoundaryWire("8", "453=1|448=party|447=D|452=3|802=1|523=sub|17=execution|")) + f.Add(groupBoundaryWire("AE", "555=1|600=leg|539=1|524=nested|804=1|545=sub|552=1|54=1|37=order|17=execution|")) + f.Fuzz(func(t *testing.T, raw []byte) { + if len(raw) > 65536 { + t.Skip() + } + _ = ParseMessageWithDataDictionary(NewMessage(), bytes.NewBuffer(raw), dictionary, dictionary) + }) +} diff --git a/message_test.go b/message_test.go index 70971fa18..1eed687c9 100644 --- a/message_test.go +++ b/message_test.go @@ -113,6 +113,45 @@ func (s *MessageSuite) TestParseOutOfOrder() { s.Nil(ParseMessage(s.msg, rawMsg)) } +func (s *MessageSuite) TestParseGroup_BodyFieldAfterNestedGroup() { + dict, dictErr := datadictionary.Parse("spec/FIX44.xml") + s.Nil(dictErr) + + // Wire layout (FIX 4.4 MassQuoteAcknowledgement): + // 117=QID QuoteID (body) + // 296=1 NoQuoteSets count (body group) + // 302=SET1 QuoteSetID (inside NoQuoteSets) + // 295=1 NoQuoteEntries count (nested group) + // 299=E1 QuoteEntryID (inside NoQuoteEntries) + // 132=100 BidPx (inside NoQuoteEntries) + // 133=101 OfferPx (inside NoQuoteEntries) + // 297=0 QuoteStatus (BODY level, AFTER the group) + rawMsg := bytes.NewBufferString( + "8=FIX.4.49=6335=b117=QID" + + "296=1302=SET1" + + "295=1299=E1132=100133=101" + + "297=1" + + "10=002") + + err := ParseMessageWithDataDictionary(s.msg, rawMsg, dict, dict) + s.Nil(err) + + rebuildBytes := s.msg.build() + expectedBytes := rawMsg.Bytes() + s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n +%s\n -%s", rebuildBytes, expectedBytes) + + // NoQuoteSets count (group delimiter) lands in Body as expected. + s.True(s.msg.Body.Has(Tag(296))) + + // QuoteStatus (297) must be a top-level body field per FIX 4.4. + s.True(s.msg.Body.Has(Tag(297)), + "QuoteStatus (297) must land at the body level; it is a body field "+ + "in MassQuoteAcknowledgement, not a member of NoQuoteSets.") + val, verr := s.msg.Body.GetInt(Tag(297)) + s.Nil(verr) + s.Equal(1, val) +} + func (s *MessageSuite) TestBuild() { s.msg.Header.SetField(tagBeginString, FIXString(BeginStringFIX44)) s.msg.Header.SetField(tagMsgType, FIXString("A")) diff --git a/parser.go b/parser.go index a3ec73993..38ac19ddc 100644 --- a/parser.go +++ b/parser.go @@ -157,7 +157,6 @@ func (p *parser) ReadMessage() (msgBytes *bytes.Buffer, err error) { } msgBytes = new(bytes.Buffer) - msgBytes.Reset() msgBytes.Write(p.buffer[:index]) p.buffer = p.buffer[index:] diff --git a/repeating_group.go b/repeating_group.go index 853cc7d35..b2dc39809 100644 --- a/repeating_group.go +++ b/repeating_group.go @@ -121,6 +121,13 @@ func (f *RepeatingGroup) Add() *Group { return g } +// Reset clears all groups from this RepeatingGroup, allowing the instance to be +// reused for a new message parse without reallocating the template and struct. +// This avoids the allocation overhead of calling NewRepeatingGroup for each parse. +func (f *RepeatingGroup) Reset() { + f.groups = f.groups[:0] +} + // Write returns tagValues for all Items in the repeating group ordered by // Group sequence and Group template order. func (f RepeatingGroup) Write() []TagValue { diff --git a/repeating_group_test.go b/repeating_group_test.go index abd316d78..5248acb24 100644 --- a/repeating_group_test.go +++ b/repeating_group_test.go @@ -17,6 +17,7 @@ package quickfix import ( "bytes" + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -291,3 +292,69 @@ func TestRepeatingGroup_ReadComplete(t *testing.T) { } } } + +func TestRepeatingGroup_Reset(t *testing.T) { + // Issue #758: RepeatingGroup should support Reset() to avoid reallocation + template := GroupTemplate{GroupElement(1), GroupElement(2)} + rg := NewRepeatingGroup(Tag(100), template) + + // Add two groups + g1 := rg.Add() + g1.SetField(Tag(1), FIXString("hello")) + g1.SetField(Tag(2), FIXString("world")) + + g2 := rg.Add() + g2.SetField(Tag(1), FIXString("foo")) + g2.SetField(Tag(2), FIXString("bar")) + + require.Equal(t, 2, rg.Len()) + + // Reset and reuse + rg.Reset() + require.Equal(t, 0, rg.Len()) + + // Add new groups after reset + g3 := rg.Add() + g3.SetField(Tag(1), FIXString("reused")) + g3.SetField(Tag(2), FIXString("instance")) + + require.Equal(t, 1, rg.Len()) + + // Verify the new group has correct data + var v1, v2 FIXString + require.Nil(t, rg.groups[0].GetField(Tag(1), &v1)) + require.Nil(t, rg.groups[0].GetField(Tag(2), &v2)) + require.Equal(t, "reused", string(v1)) + require.Equal(t, "instance", string(v2)) + + // Tag and template should be unchanged + require.Equal(t, Tag(100), rg.Tag()) + require.Equal(t, 2, len(rg.template)) +} + +func TestRepeatingGroup_ResetMultipleCycles(t *testing.T) { + template := GroupTemplate{GroupElement(1)} + rg := NewRepeatingGroup(Tag(50), template) + + // Simulate multiple message parses without reallocation + for cycle := 0; cycle < 3; cycle++ { + numGroups := cycle + 1 + for i := 0; i < numGroups; i++ { + g := rg.Add() + g.SetField(Tag(1), FIXString(fmt.Sprintf("cycle-%d-group-%d", cycle, i))) + } + + require.Equal(t, numGroups, rg.Len()) + + // Verify data + for i := 0; i < numGroups; i++ { + var v FIXString + require.Nil(t, rg.groups[i].GetField(Tag(1), &v)) + require.Equal(t, fmt.Sprintf("cycle-%d-group-%d", cycle, i), string(v)) + } + + // Reset for next cycle + rg.Reset() + require.Equal(t, 0, rg.Len()) + } +} diff --git a/session_factory.go b/session_factory.go index 3d55bc116..9c77c8b86 100644 --- a/session_factory.go +++ b/session_factory.go @@ -147,16 +147,16 @@ func (f sessionFactory) newSession( if s.transportDataDictionary, err = datadictionary.Parse(transportDataDictionaryPath); err != nil { err = errors.Wrapf( - err, "problem parsing XML datadictionary path '%v' for setting '%v", - settings.settings[config.TransportDataDictionary], config.TransportDataDictionary, + err, "problem parsing XML datadictionary path '%v' for setting '%v'", + string(settings.settings[config.TransportDataDictionary]), config.TransportDataDictionary, ) return } if s.appDataDictionary, err = datadictionary.Parse(appDataDictionaryPath); err != nil { err = errors.Wrapf( - err, "problem parsing XML datadictionary path '%v' for setting '%v", - settings.settings[config.AppDataDictionary], config.AppDataDictionary, + err, "problem parsing XML datadictionary path '%v' for setting '%v'", + string(settings.settings[config.AppDataDictionary]), config.AppDataDictionary, ) return } @@ -171,8 +171,8 @@ func (f sessionFactory) newSession( if s.appDataDictionary, err = datadictionary.Parse(dataDictionaryPath); err != nil { err = errors.Wrapf( - err, "problem parsing XML datadictionary path '%v' for setting '%v", - settings.settings[config.DataDictionary], config.DataDictionary, + err, "problem parsing XML datadictionary path '%v' for setting '%v'", + string(settings.settings[config.DataDictionary]), config.DataDictionary, ) return } diff --git a/session_factory_test.go b/session_factory_test.go index 9f6762302..3d64e2fce 100644 --- a/session_factory_test.go +++ b/session_factory_test.go @@ -744,3 +744,11 @@ func (s *SessionFactorySuite) TestPersistMessages() { s.Equal(test.expected, session.DisableMessagePersist) } } + +func (s *SessionFactorySuite) TestBadDictionaryPath() { + s.SetupTest() + s.SessionSettings.Set(config.DataDictionary, "file/does/not/exist.xml") + _, err := s.newSession(s.SessionID, s.MessageStoreFactory, s.SessionSettings, s.LogFactory, s.App) + s.NotNil(err) + s.Contains(err.Error(), "problem parsing XML datadictionary path 'file/does/not/exist.xml' for setting 'DataDictionary':") +} diff --git a/testdata/fuzz/FuzzDictionaryGroupParsing/1daff26a3835abc3 b/testdata/fuzz/FuzzDictionaryGroupParsing/1daff26a3835abc3 new file mode 100644 index 000000000..a897c3c30 --- /dev/null +++ b/testdata/fuzz/FuzzDictionaryGroupParsing/1daff26a3835abc3 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("8=\x01") diff --git a/testdata/fuzz/FuzzDictionaryGroupParsing/5b0eabea2da9142a b/testdata/fuzz/FuzzDictionaryGroupParsing/5b0eabea2da9142a new file mode 100644 index 000000000..d0447e771 --- /dev/null +++ b/testdata/fuzz/FuzzDictionaryGroupParsing/5b0eabea2da9142a @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("8=\x019=\x0135=\x0110=\x01")