-
- Notifications
You must be signed in to change notification settings - Fork 81
Intro ediReader struct, its stack definition and rawSeg definition plus some basic stack operations #98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Intro ediReader struct, its stack definition and rawSeg definition plus some basic stack operations #98
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| package edi | ||
| | ||
| import ( | ||
| "bufio" | ||
| "fmt" | ||
| | ||
| "github.com/jf-tech/omniparser/idr" | ||
| ) | ||
| | ||
| type rawSegElem struct { | ||
| elemIndex int // for this piece of data, the index of which element it belongs to. 1-based. | ||
| compIndex int // for this piece of data, the index of which component it belongs to. 1-based. | ||
| data []byte | ||
| } | ||
| | ||
| type rawSeg struct { | ||
| name string // name of the segment, e.g. 'ISA', 'GS', etc. | ||
| raw []byte // the raw data of the entire segment, including segment delimiter. | ||
| elems []rawSegElem // all the broken down pieces of elements of the segment. | ||
| } | ||
| | ||
| const ( | ||
| defaultElemsPerSeg = 20 | ||
| ) | ||
| | ||
| func newRawSeg() rawSeg { | ||
| return rawSeg{ | ||
| elems: make([]rawSegElem, 0, defaultElemsPerSeg), | ||
| } | ||
| } | ||
| | ||
| type stackEntry struct { | ||
| segDecl *segDecl // the current stack entry's segment decl | ||
| segNode *idr.Node // the current stack entry segment's IDR node | ||
| curChild int // which child segment is the current segment is processing. | ||
| occurred int // how many times the current segment is fully processed. | ||
| } | ||
| | ||
| const ( | ||
| defaultStackDepth = 50 | ||
jf-tech marked this conversation as resolved. Show resolved Hide resolved | ||
| ) | ||
| | ||
| func newStack() []stackEntry { | ||
| return make([]stackEntry, 0, defaultStackDepth) | ||
| } | ||
| | ||
| type ediReader struct { | ||
| filename string | ||
| scanner *bufio.Scanner | ||
| segDelim string | ||
| elemDelim string | ||
| compDelim *string | ||
| releaseChar *rune | ||
| stack []stackEntry | ||
| target *idr.Node | ||
| runeCount int | ||
| unprocessedSegData rawSeg | ||
| } | ||
| | ||
| func inRange(i, lowerBoundInclusive, upperBoundInclusive int) bool { | ||
| return i >= lowerBoundInclusive && i <= upperBoundInclusive | ||
| } | ||
| | ||
| func (r *ediReader) resetRawSeg() { | ||
| r.unprocessedSegData.name = "" | ||
| r.unprocessedSegData.raw = nil | ||
| r.unprocessedSegData.elems = r.unprocessedSegData.elems[:0] | ||
| } | ||
| | ||
| // stackTop returns the pointer to the 'frame'-th stack entry from the top. | ||
| // 'frame' is optional, if not specified, default 0 (aka the very top of | ||
| // the stack) is assumed. Note caller NEVER owns the memory of the returned | ||
| // entry, thus caller can use the pointer and its data values inside locally | ||
| // but should never cache/save it somewhere for later usage. | ||
| func (r *ediReader) stackTop(frame ...int) *stackEntry { | ||
| nth := 0 | ||
| if len(frame) == 1 { | ||
| nth = frame[0] | ||
| } | ||
| if !inRange(nth, 0, len(r.stack)-1) { | ||
| panic(fmt.Sprintf("frame requested: %d, but stack length: %d", nth, len(r.stack))) | ||
| } | ||
| return &r.stack[len(r.stack)-nth-1] | ||
| } | ||
| | ||
| // shrinkStack removes the top frame of the stack and returns the pointer to the NEW TOP | ||
| // FRAME to caller. Note caller NEVER owns the memory of the returned entry, thus caller can | ||
| // use the pointer and its data values inside locally but should never cache/save it somewhere | ||
| // for later usage. | ||
| func (r *ediReader) shrinkStack() *stackEntry { | ||
| if len(r.stack) < 1 { | ||
| panic("stack length is empty") | ||
| } | ||
| r.stack = r.stack[:len(r.stack)-1] | ||
| if len(r.stack) < 1 { | ||
| return nil | ||
| } | ||
| return &r.stack[len(r.stack)-1] | ||
| } | ||
| | ||
| // growStack adds a new stack entry to the top of the stack. | ||
| func (r *ediReader) growStack(e stackEntry) { | ||
| r.stack = append(r.stack, e) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| package edi | ||
| | ||
| import ( | ||
| "testing" | ||
| | ||
| "github.com/stretchr/testify/assert" | ||
| | ||
| "github.com/jf-tech/omniparser/idr" | ||
| ) | ||
| | ||
| func TestRawSeg(t *testing.T) { | ||
| rawSegName := "test" | ||
| rawSegData := []byte("test data") | ||
| r := ediReader{ | ||
| unprocessedSegData: newRawSeg(), | ||
| } | ||
| assert.Equal(t, "", r.unprocessedSegData.name) | ||
| assert.Nil(t, r.unprocessedSegData.raw) | ||
| assert.Equal(t, 0, len(r.unprocessedSegData.elems)) | ||
| assert.Equal(t, defaultElemsPerSeg, cap(r.unprocessedSegData.elems)) | ||
| r.unprocessedSegData.name = rawSegName | ||
| r.unprocessedSegData.raw = rawSegData | ||
| r.unprocessedSegData.elems = append( | ||
| r.unprocessedSegData.elems, rawSegElem{1, 1, rawSegData[0:4]}, rawSegElem{2, 1, rawSegData[5:]}) | ||
| r.resetRawSeg() | ||
| assert.Equal(t, "", r.unprocessedSegData.name) | ||
| assert.Nil(t, r.unprocessedSegData.raw) | ||
| assert.Equal(t, 0, len(r.unprocessedSegData.elems)) | ||
| assert.Equal(t, defaultElemsPerSeg, cap(r.unprocessedSegData.elems)) | ||
| } | ||
| | ||
| // Adding a benchmark for rawSeg operation to ensure there is no alloc: | ||
| // BenchmarkRawSeg-8 81410766 13.9 ns/op 0 B/op 0 allocs/op | ||
| func BenchmarkRawSeg(b *testing.B) { | ||
| rawSegName := "test" | ||
| rawSegData := []byte("test data") | ||
| r := ediReader{ | ||
| unprocessedSegData: newRawSeg(), | ||
| } | ||
| for i := 0; i < b.N; i++ { | ||
| r.resetRawSeg() | ||
| r.unprocessedSegData.name = rawSegName | ||
| r.unprocessedSegData.raw = rawSegData | ||
| r.unprocessedSegData.elems = append( | ||
| r.unprocessedSegData.elems, rawSegElem{1, 1, rawSegData[0:4]}, rawSegElem{2, 1, rawSegData[5:]}) | ||
| } | ||
| } | ||
| | ||
| func TestStack(t *testing.T) { | ||
| r := ediReader{ | ||
| stack: newStack(), | ||
| } | ||
| assert.Equal(t, 0, len(r.stack)) | ||
| assert.Equal(t, defaultStackDepth, cap(r.stack)) | ||
| // try to access top of stack while there is nothing in it => panic. | ||
| assert.PanicsWithValue(t, | ||
| "frame requested: 0, but stack length: 0", | ||
| func() { | ||
| r.stackTop() | ||
| }) | ||
| // try to shrink empty stack => panic. | ||
| assert.PanicsWithValue(t, | ||
| "stack length is empty", | ||
| func() { | ||
| r.shrinkStack() | ||
| }) | ||
| newEntry1 := stackEntry{ | ||
| segDecl: &segDecl{}, | ||
| segNode: idr.CreateNode(idr.TextNode, "test"), | ||
| curChild: 5, | ||
| occurred: 10, | ||
| } | ||
| r.growStack(newEntry1) | ||
| assert.Equal(t, 1, len(r.stack)) | ||
| assert.Equal(t, newEntry1, *r.stackTop()) | ||
| newEntry2 := stackEntry{ | ||
| segDecl: &segDecl{}, | ||
| segNode: idr.CreateNode(idr.TextNode, "test 2"), | ||
| curChild: 10, | ||
| occurred: 20, | ||
| } | ||
| r.growStack(newEntry2) | ||
| assert.Equal(t, 2, len(r.stack)) | ||
| assert.Equal(t, newEntry2, *r.stackTop()) | ||
| // try to access a frame that doesn't exist => panic. | ||
| assert.PanicsWithValue(t, | ||
| "frame requested: 2, but stack length: 2", | ||
| func() { | ||
| r.stackTop(2) | ||
| }) | ||
| assert.Equal(t, newEntry1, *r.shrinkStack()) | ||
| assert.Nil(t, r.shrinkStack()) | ||
| } | ||
| | ||
| // Adding a benchmark for stack operation to ensure there is no alloc: | ||
| // BenchmarkStack-8 12901227 89.0 ns/op 0 B/op 0 allocs/op | ||
| func BenchmarkStack(b *testing.B) { | ||
| r := ediReader{ | ||
| stack: newStack(), | ||
| } | ||
| for i := 0; i < b.N; i++ { | ||
| for j := 0; j < 20; j++ { | ||
| r.growStack(stackEntry{}) | ||
| } | ||
| for r.shrinkStack() != nil { | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge. Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.