From 72964bfa1f10b183de2a1d6577aad09d81609ae3 Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Tue, 25 Apr 2023 17:42:05 +0100 Subject: Replace readString in walk/read.go with a faster implementation that makes better use of the buffer system --- walk/read.go | 190 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 100 insertions(+), 90 deletions(-) (limited to 'walk/read.go') diff --git a/walk/read.go b/walk/read.go index 58dfcae..c98b941 100644 --- a/walk/read.go +++ b/walk/read.go @@ -8,31 +8,14 @@ import ( "fmt" ) -type ReadAction interface { - String() string -} - -type ActionReadValue struct {} -func (_ ActionReadValue) String() string { - return "read" -} - -type ActionAppendPath struct { - atoms []Atom -} -func (action ActionAppendPath) String() string { - return fmt.Sprintf("append(%v)", action.atoms) -} - -type ActionPopPath struct {} -func (_ ActionPopPath) String() string { - return "pop" -} - -type ActionIncrementPath struct {} -func (_ ActionIncrementPath) String() string { - return "increment" -} +type ReadAction int +const ( + ActionReadValue ReadAction = iota + ActionAppendPath + ActionPopPath + ActionIncrementPath + ActionAppendPathNull +) type JSONInStructure int const ( @@ -41,6 +24,8 @@ const ( JSONInArray JSONInString JSONInValueEnd + JSONInKey + JSONInKeyEnd ) type JSONIn struct { @@ -130,41 +115,6 @@ func (in *JSONIn) require(criterion rune) { } } -func (in *JSONIn) readString(out []Atom) []Atom { - // TODO: improve - out = append(out, NewAtomStringTerminal()) - for { - r, _, err := in.reader.ReadRune() - if err != nil { - panic("Missing closing terminal in string input: " + err.Error()) - } - if r == '"' { - break - } - if r == '\\' { - r, _, err = in.reader.ReadRune() - if err != nil { - panic("Missing rune after \\") - } - if len(out) == cap(out) { - newOut := make([]Atom, len(out), cap(out) * 2) - copy(newOut, out) - out = newOut - } - out = append(out, NewAtomStringRune(r)) - continue - } - if len(out) == cap(out) { - newOut := make([]Atom, len(out), cap(out) * 2) - copy(newOut, out) - out = newOut - } - out = append(out, NewAtomStringRune(r)) - } - out = append(out, NewAtomStringTerminal()) - return out -} - // Returns the first full value of a list of atoms and also a boolean to indicate if there isn't a value at the beginning func firstValue(atoms []Atom) ([]Atom, bool) { if len(atoms) == 0 { @@ -199,7 +149,7 @@ func (in *JSONIn) Read() (WalkItem, error) { } } action := in.actionBuffer[in.actionIndex] - switch a := action.(type) { + switch action { case ActionReadValue: value, incomplete := firstValue(in.readBuffer[in.readIndex:]) if incomplete { @@ -229,7 +179,31 @@ func (in *JSONIn) Read() (WalkItem, error) { Path: in.path, }, nil case ActionAppendPath: - in.path = append(in.path, a.atoms...) + value, incomplete := firstValue(in.readBuffer[in.readIndex:]) + if incomplete { + if in.readIndex == 0 { + newReadBuffer := make([]Atom, len(in.readBuffer), in.readBufferCapacity * 2) + in.readBufferCapacity *= 2 + copy(newReadBuffer, in.readBuffer) + in.readBuffer = newReadBuffer + structure, _ := in.fillReadBuffer(in.structure) + in.structure = structure + continue actionLoop + } + copy(in.readBuffer, in.readBuffer[in.readIndex:]) + in.readBuffer = in.readBuffer[:len(in.readBuffer) - in.readIndex] + in.readIndex = 0 + copy(in.actionBuffer, in.actionBuffer[in.actionIndex:]) + in.actionBuffer = in.actionBuffer[:len(in.actionBuffer) - in.actionIndex] + in.actionIndex = 0 + structure, _ := in.fillReadBuffer(in.structure) + in.structure = structure + continue actionLoop + } + in.readIndex += len(value) + in.path = append(in.path, value...) + case ActionAppendPathNull: + in.path = append(in.path, NewAtomNull()) case ActionPopPath: in.popPath() case ActionIncrementPath: @@ -275,6 +249,12 @@ func (in *JSONIn) fillReadBuffer(structure []JSONInStructure) ([]JSONInStructure case JSONInValueEnd: structure = structure[:len(structure) - 1] goto valueEnd + case JSONInKey: + structure = structure[:len(structure) - 1] + goto key + case JSONInKeyEnd: + structure = structure[:len(structure) - 1] + goto keyEnd case JSONInMap: goto mapValue case JSONInArray: @@ -291,43 +271,43 @@ func (in *JSONIn) fillReadBuffer(structure []JSONInStructure) ([]JSONInStructure switch r { case 'n': in.requireString("ull") - in.pushActionBuffer(ActionReadValue{}) + in.pushActionBuffer(ActionReadValue) if in.pushReadBuffer(NewAtomNull()) { return append(structure, JSONInValueEnd), nil } goto valueEnd case 'f': in.requireString("alse") - in.pushActionBuffer(ActionReadValue{}) + in.pushActionBuffer(ActionReadValue) if in.pushReadBuffer(NewAtomBool(false)) { return append(structure, JSONInValueEnd), nil } goto valueEnd case 't': in.requireString("rue") - in.pushActionBuffer(ActionReadValue{}) + in.pushActionBuffer(ActionReadValue) if in.pushReadBuffer(NewAtomBool(true)) { return append(structure, JSONInValueEnd), nil } goto valueEnd case '"': - in.pushActionBuffer(ActionReadValue{}) + in.pushActionBuffer(ActionReadValue) if in.pushReadBuffer(NewAtomStringTerminal()) { return append(structure, JSONInString), nil } goto string case '{': structure = append(structure, JSONInMap) - in.pushActionBuffer(ActionReadValue{}) - in.pushActionBuffer(ActionAppendPath {[]Atom{NewAtomNull()}}) + in.pushActionBuffer(ActionReadValue) + in.pushActionBuffer(ActionAppendPathNull) if in.pushReadBuffer(NewAtomTerminal(MapBegin)) { return structure, nil } goto mapValue case '[': structure = append(structure, JSONInArray) - in.pushActionBuffer(ActionReadValue{}) - in.pushActionBuffer(ActionAppendPath {[]Atom{NewAtomNull()}}) + in.pushActionBuffer(ActionReadValue) + in.pushActionBuffer(ActionAppendPathNull) if in.pushReadBuffer(NewAtomTerminal(ArrayBegin)) { return structure, nil } @@ -351,7 +331,7 @@ func (in *JSONIn) fillReadBuffer(structure []JSONInStructure) ([]JSONInStructure if parseError != nil { panic("Invalid number") } - in.pushActionBuffer(ActionReadValue{}) + in.pushActionBuffer(ActionReadValue) if in.pushReadBuffer(NewAtomNumber(number)) { return append(structure, JSONInValueEnd), nil } @@ -385,6 +365,42 @@ func (in *JSONIn) fillReadBuffer(structure []JSONInStructure) ([]JSONInStructure } goto string } + key: { + r, _, err := in.reader.ReadRune() + if err != nil { + panic("Missing closing terminal in string input: " + err.Error()) + } + if r == '"' { + if in.pushReadBuffer(NewAtomStringTerminal()) { + return append(structure, JSONInKeyEnd), nil + } + goto keyEnd + } + if r == '\\' { + r, _, err = in.reader.ReadRune() + if err != nil { + panic("Missing rune after \\") + } + if in.pushReadBuffer(NewAtomStringRune(r)) { + return append(structure, JSONInKey), nil + } + goto key + } + if in.pushReadBuffer(NewAtomStringRune(r)) { + return append(structure, JSONInKey), nil + } + goto key + } + keyEnd: { + r, err := in.nextNonWsRune() + if err != nil { + panic("Expected : got: " + err.Error()) + } + if r != ':' { + panic("Expected : after key") + } + goto value + } valueEnd: { r, err := in.nextNonWsRune() if err != nil { @@ -395,16 +411,16 @@ func (in *JSONIn) fillReadBuffer(structure []JSONInStructure) ([]JSONInStructure panic("More input after root JSON object ends") } else if underState == JSONInMap && r == '}' { structure = structure[:len(structure) - 1] - in.pushActionBuffer(ActionPopPath{}) - in.pushActionBuffer(ActionReadValue{}) + in.pushActionBuffer(ActionPopPath) + in.pushActionBuffer(ActionReadValue) if in.pushReadBuffer(NewAtomTerminal(MapEnd)) { return append(structure, JSONInValueEnd), nil } goto valueEnd } else if underState == JSONInArray && r == ']' { structure = structure[:len(structure) - 1] - in.pushActionBuffer(ActionPopPath{}) - in.pushActionBuffer(ActionReadValue{}) + in.pushActionBuffer(ActionPopPath) + in.pushActionBuffer(ActionReadValue) if in.pushReadBuffer(NewAtomTerminal(ArrayEnd)) { return append(structure, JSONInValueEnd), nil } @@ -416,14 +432,14 @@ func (in *JSONIn) fillReadBuffer(structure []JSONInStructure) ([]JSONInStructure goto valueStart } mapValue: { - in.pushActionBuffer(ActionPopPath{}) + in.pushActionBuffer(ActionPopPath) r, err := in.nextNonWsRune() if err != nil { panic("Missing value inside object") } if r == '}' { structure = structure[:len(structure) - 1] - in.pushActionBuffer(ActionReadValue{}) + in.pushActionBuffer(ActionReadValue) if in.pushReadBuffer(NewAtomTerminal(MapEnd)) { return append(structure, JSONInValueEnd), nil } @@ -432,17 +448,11 @@ func (in *JSONIn) fillReadBuffer(structure []JSONInStructure) ([]JSONInStructure if r != '"' { panic("Expected key found something else") } - var keyAtoms []Atom - keyAtoms = in.readString(keyAtoms) - in.pushActionBuffer(ActionAppendPath {keyAtoms}) - r, err = in.nextNonWsRune() - if err != nil { - panic("Expected : got: " + err.Error()) + in.pushActionBuffer(ActionAppendPath) + if in.pushReadBuffer(NewAtomStringTerminal()) { + return append(structure, JSONInKey), nil } - if r != ':' { - panic("Expected : after key") - } - goto value + goto key } arrayValue: { r, err := in.nextNonWsRune() @@ -451,15 +461,15 @@ func (in *JSONIn) fillReadBuffer(structure []JSONInStructure) ([]JSONInStructure } if r == ']' { structure = structure[:len(structure) - 1] - in.pushActionBuffer(ActionPopPath{}) - in.pushActionBuffer(ActionReadValue{}) + in.pushActionBuffer(ActionPopPath) + in.pushActionBuffer(ActionReadValue) if in.pushReadBuffer(NewAtomTerminal(ArrayEnd)) { return append(structure, JSONInValueEnd), nil } goto valueEnd } in.reader.UnreadRune() - in.pushActionBuffer(ActionIncrementPath{}) + in.pushActionBuffer(ActionIncrementPath) goto value } } -- cgit v1.2.3