Prechádzať zdrojové kódy

Better unicode input. Tests READ_BUFFER.

Steve Thielemann 2 rokov pred
rodič
commit
99c3cec7f1
3 zmenil súbory, kde vykonal 125 pridanie a 48 odobranie
  1. 22 3
      door/door_test.go
  2. 98 1
      door/input.go
  3. 5 44
      door/input_linux.go

+ 22 - 3
door/door_test.go

@@ -7,6 +7,7 @@ import (
 	"log"
 	"net"
 	"os"
+	"strings"
 	"testing"
 	"time"
 )
@@ -18,7 +19,7 @@ import (
 // go test -c; ./door.test -test.run DoorCP437 -test.v
 
 const KEEP_LOGS bool = false
-const VERBOSE_TEST bool = true // false
+const VERBOSE_TEST bool = false
 
 func TestGoto(t *testing.T) {
 	GotoMap := map[string][]int{"\x1b[10;20H": {20, 10},
@@ -260,6 +261,20 @@ func InputTests(t *testing.T, server net.Conn, d *Door, mode string) {
 		"\U0001f920\u2615": []rune{'\U0001f920', '\u2615'},
 	}
 
+	/*
+		Test input buffer processing.
+		Create a string with unicode that doesn't fit into the
+		entire read buffer.  And build the proper response to match.
+	*/
+
+	// x = number of missing bytes (from unicode symbol).
+	for x := 1; x <= 3; x++ {
+		tosend := strings.Repeat(" ", READ_SIZE-x) + "\U0001f920"
+		receive := []rune(strings.Repeat(" ", READ_SIZE-x))
+		receive = append(receive, '\U0001f920')
+		keytest[tosend] = receive
+	}
+
 	keyextest := map[string][]Extended{
 		"\x00\x50\x00\x48\x00\x4b\x00\x4d": []Extended{DOWN_ARROW, UP_ARROW, LEFT_ARROW, RIGHT_ARROW},
 		"\x00\x47\x00\x4f\x00\x49\x00\x51": []Extended{HOME, END, PAGE_UP, PAGE_DOWN},
@@ -328,7 +343,7 @@ func InputTests(t *testing.T, server net.Conn, d *Door, mode string) {
 		}
 
 		if len(recv) != len(get) {
-			t.Errorf("Sent %#v, LEN expected %#v, got %#v", send, get, recv)
+			t.Errorf("FAILURE: Sent %#v, LEN expected %#v, got %#v", send, get, recv)
 		} else {
 			matches := true
 			for idx, i := range get {
@@ -338,7 +353,11 @@ func InputTests(t *testing.T, server net.Conn, d *Door, mode string) {
 				}
 			}
 			if !matches {
-				t.Errorf("Sent %#v, MATCH expected %#v, got %#v", send, get, recv)
+				t.Errorf("FAILURE: Sent %#v, MATCH expected %#v, got %#v", send, get, recv)
+			} else {
+				if VERBOSE_TEST {
+					t.Logf("Success.")
+				}
 			}
 		}
 	}

+ 98 - 1
door/input.go

@@ -1,7 +1,9 @@
 package door
 
 import (
+	"bufio"
 	"fmt"
+	"io"
 	"log"
 	"strconv"
 	"strings"
@@ -14,9 +16,10 @@ import (
 var ErrInactivity error = fmt.Errorf("Inactivity")
 var ErrTimeout error = fmt.Errorf("Timeout")
 var ErrDisconnected error = fmt.Errorf("Disconnected")
-
 var DefaultTimeout time.Duration = time.Duration(60) * time.Second
 
+const READ_SIZE = 16 // Size of read buffer
+
 // put the common input routines here
 var readerBuffer []rune
 
@@ -51,6 +54,100 @@ var extendedKeys map[string]Extended = map[string]Extended{
 	"Ot":   F5, // syncterm
 }
 
+func HasEnoughBytesForUnicode(runeread *bufio.Reader) bool {
+	// Does the buffer have enough bytes for valid UTF-8?
+	// https://en.wikipedia.org/wiki/UTF-8
+	if runeread.Buffered() == 0 {
+		return true
+	}
+	// must be at least 1, so:
+	peek, err := runeread.Peek(1)
+	if err != nil {
+		return true
+	}
+	b := peek[0]
+	if b&0x80 == 0 {
+		// single byte unicode - OK!
+		return true
+	}
+	if b&0b11100000 == 0b11000000 {
+		// need 2 bytes
+		if runeread.Buffered() >= 2 {
+			return true
+		}
+		return false
+	}
+	if b&0b11110000 == 0b11100000 {
+		if runeread.Buffered() >= 3 {
+			return true
+		}
+		return false
+	}
+	if b&0b11111000 == 0b11110000 {
+		if runeread.Buffered() >= 4 {
+			return true
+		}
+		return false
+	}
+
+	// Invalid UTF-8 encoding.
+	return true
+}
+
+func ReadRune(d *Door, runeread *bufio.Reader, timeout bool) {
+	var input rune
+	var err error
+
+	// Is this unicode?
+
+	err = nil
+	for err == nil {
+		//RuneRead:
+
+		enough := HasEnoughBytesForUnicode(runeread)
+
+		if (!enough) && (!timeout) {
+			// We don't have enought, but there wasn't a timeout --
+			// go try and read more.
+			return
+		}
+
+		input, _, err = runeread.ReadRune()
+
+		if err == io.EOF {
+			break // continue
+		}
+		if err != nil {
+			if DEBUG_INPUT {
+				log.Printf("ReadRune: %#v\n", err)
+			}
+			// errors EOF
+			break // continue // break for loop
+		}
+
+		if input == unicode.ReplacementChar {
+			// possibly incomplete rune received.
+
+			runeread.UnreadRune()
+			b, _ := runeread.ReadByte()
+			if DEBUG_INPUT {
+				log.Printf("Reader (byte) >> %x\n", b)
+			}
+			readerBuffer = append(readerBuffer, rune(b))
+			// d.readerChannel <- rune(b)
+		} else {
+			if DEBUG_INPUT {
+				log.Printf("Reader >> %x\n", input)
+			}
+			readerBuffer = append(readerBuffer, input)
+			// d.readerChannel <- input
+		}
+		// process(d, true)
+
+	} // goto RuneRead
+	process(d, !timeout)
+}
+
 func process(d *Door, newRune bool) {
 	// This is like GetKey
 	var rlen int

+ 5 - 44
door/input_linux.go

@@ -3,12 +3,10 @@ package door
 import (
 	"bufio"
 	"bytes"
-	"io"
 	"log"
 	"strings"
 	"syscall"
 	"time"
-	"unicode"
 )
 
 var ReaderInterval = time.Duration(200) * time.Millisecond
@@ -37,8 +35,6 @@ func RuneToInt8(r rune) int8 {
 	return int8(r)
 }
 
-const READ_SIZE = 16 // Size of read buffer
-
 // go routine Reader for input
 // This "times out" every ReaderTimeval
 func Reader(d *Door) {
@@ -94,7 +90,8 @@ func Reader(d *Door) {
 
 		if v == 0 {
 			// timeout
-			process(d, false)
+			ReadRune(d, runeread, true)
+			// process(d, false)
 			continue
 		}
 
@@ -128,47 +125,11 @@ func Reader(d *Door) {
 			log.Printf("Reader << %d, %#v\n", r, readbuffer[:r])
 		}
 
+		// write the received bytes into runebuffer.
 		runebuffer.Write(readbuffer[:r])
-		var input rune
-
-		// Is this unicode?
-
-		err = nil
-		for err == nil {
-			//RuneRead:
-
-			input, _, err = runeread.ReadRune()
-
-			if err == io.EOF {
-				break // continue
-			}
-			if err != nil {
-				if DEBUG_INPUT {
-					log.Printf("ReadRune: %#v\n", err)
-				}
-				// errors EOF
-				break // continue // break for loop
-			}
-			if input == unicode.ReplacementChar {
-				runeread.UnreadRune()
-				b, _ := runeread.ReadByte()
-				if DEBUG_INPUT {
-					log.Printf("Reader (byte) >> %x\n", b)
-				}
-				readerBuffer = append(readerBuffer, rune(b))
-				// d.readerChannel <- rune(b)
-			} else {
-				if DEBUG_INPUT {
-					log.Printf("Reader >> %x\n", input)
-				}
-				readerBuffer = append(readerBuffer, input)
-				// d.readerChannel <- input
-			}
-			// process(d, true)
-
-		} // goto RuneRead
 
-		process(d, true)
+		ReadRune(d, runeread, false)
+		// process(d, true)
 
 		// buffer = append(buffer, readone[0])