From 72964bfa1f10b183de2a1d6577aad09d81609ae3 Mon Sep 17 00:00:00 2001
From: Charlie Stanton <charlie@shtanton.xyz>
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')

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