diff options
Diffstat (limited to 'libgo/go/exp')
-rw-r--r-- | libgo/go/exp/inotify/inotify_linux_test.go | 20 | ||||
-rw-r--r-- | libgo/go/exp/norm/composition.go | 85 | ||||
-rw-r--r-- | libgo/go/exp/norm/composition_test.go | 32 | ||||
-rw-r--r-- | libgo/go/exp/norm/input.go | 10 | ||||
-rw-r--r-- | libgo/go/exp/norm/iter.go | 286 | ||||
-rw-r--r-- | libgo/go/exp/norm/iter_test.go | 186 | ||||
-rw-r--r-- | libgo/go/exp/norm/normalize.go | 15 | ||||
-rw-r--r-- | libgo/go/exp/norm/normalize_test.go | 55 | ||||
-rw-r--r-- | libgo/go/exp/norm/normregtest.go | 11 | ||||
-rw-r--r-- | libgo/go/exp/proxy/socks5.go | 6 | ||||
-rw-r--r-- | libgo/go/exp/terminal/terminal.go | 4 | ||||
-rw-r--r-- | libgo/go/exp/winfsnotify/winfsnotify_test.go | 9 |
12 files changed, 656 insertions, 63 deletions
diff --git a/libgo/go/exp/inotify/inotify_linux_test.go b/libgo/go/exp/inotify/inotify_linux_test.go index c2160fc6537..d41d66bfacd 100644 --- a/libgo/go/exp/inotify/inotify_linux_test.go +++ b/libgo/go/exp/inotify/inotify_linux_test.go @@ -7,6 +7,7 @@ package inotify import ( + "io/ioutil" "os" "testing" "time" @@ -16,16 +17,19 @@ func TestInotifyEvents(t *testing.T) { // Create an inotify watcher instance and initialize it watcher, err := NewWatcher() if err != nil { - t.Fatalf("NewWatcher() failed: %s", err) + t.Fatalf("NewWatcher failed: %s", err) } - t.Logf("NEEDS TO BE CONVERTED TO NEW GO TOOL") // TODO - return + dir, err := ioutil.TempDir("", "inotify") + if err != nil { + t.Fatalf("TempDir failed: %s", err) + } + defer os.RemoveAll(dir) // Add a watch for "_test" - err = watcher.Watch("_test") + err = watcher.Watch(dir) if err != nil { - t.Fatalf("Watcher.Watch() failed: %s", err) + t.Fatalf("Watch failed: %s", err) } // Receive errors on the error channel on a separate goroutine @@ -35,7 +39,7 @@ func TestInotifyEvents(t *testing.T) { } }() - const testFile string = "_test/TestInotifyEvents.testfile" + testFile := dir + "/TestInotifyEvents.testfile" // Receive events on the event channel on a separate goroutine eventstream := watcher.Event @@ -58,7 +62,7 @@ func TestInotifyEvents(t *testing.T) { // This should add at least one event to the inotify event queue _, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) if err != nil { - t.Fatalf("creating test file failed: %s", err) + t.Fatalf("creating test file: %s", err) } // We expect this event to be received almost immediately, but let's wait 1 s to be sure @@ -95,7 +99,7 @@ func TestInotifyClose(t *testing.T) { t.Fatal("double Close() test failed: second Close() call didn't return") } - err := watcher.Watch("_test") + err := watcher.Watch(os.TempDir()) if err == nil { t.Fatal("expected error on Watch() after Close(), got nil") } diff --git a/libgo/go/exp/norm/composition.go b/libgo/go/exp/norm/composition.go index ccff4670602..2cbe1ac730e 100644 --- a/libgo/go/exp/norm/composition.go +++ b/libgo/go/exp/norm/composition.go @@ -66,6 +66,18 @@ func (rb *reorderBuffer) flush(out []byte) []byte { return out } +// flushCopy copies the normalized segment to buf and resets rb. +// It returns the number of bytes written to buf. +func (rb *reorderBuffer) flushCopy(buf []byte) int { + p := 0 + for i := 0; i < rb.nrune; i++ { + runep := rb.rune[i] + p += copy(buf[p:], rb.byte[runep.pos:runep.pos+runep.size]) + } + rb.reset() + return p +} + // insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class. // It returns false if the buffer is not large enough to hold the rune. // It is used internally by insert and insertString only. @@ -96,32 +108,41 @@ func (rb *reorderBuffer) insertOrdered(info runeInfo) bool { // insert inserts the given rune in the buffer ordered by CCC. // It returns true if the buffer was large enough to hold the decomposed rune. func (rb *reorderBuffer) insert(src input, i int, info runeInfo) bool { - if info.size == 3 { - if rune := src.hangul(i); rune != 0 { - return rb.decomposeHangul(rune) - } + if rune := src.hangul(i); rune != 0 { + return rb.decomposeHangul(rune) } if info.hasDecomposition() { - dcomp := info.decomposition() - rb.tmpBytes = inputBytes(dcomp) - for i := 0; i < len(dcomp); { - info = rb.f.info(&rb.tmpBytes, i) - pos := rb.nbyte - if !rb.insertOrdered(info) { - return false - } - end := i + int(info.size) - copy(rb.byte[pos:], dcomp[i:end]) - i = end - } - } else { - // insertOrder changes nbyte + return rb.insertDecomposed(info.decomposition()) + } + return rb.insertSingle(src, i, info) +} + +// insertDecomposed inserts an entry in to the reorderBuffer for each rune +// in dcomp. dcomp must be a sequence of decomposed UTF-8-encoded runes. +func (rb *reorderBuffer) insertDecomposed(dcomp []byte) bool { + saveNrune, saveNbyte := rb.nrune, rb.nbyte + rb.tmpBytes = inputBytes(dcomp) + for i := 0; i < len(dcomp); { + info := rb.f.info(&rb.tmpBytes, i) pos := rb.nbyte if !rb.insertOrdered(info) { + rb.nrune, rb.nbyte = saveNrune, saveNbyte return false } - src.copySlice(rb.byte[pos:], i, i+int(info.size)) + i += copy(rb.byte[pos:], dcomp[i:i+int(info.size)]) + } + return true +} + +// insertSingle inserts an entry in the reorderBuffer for the rune at +// position i. info is the runeInfo for the rune at position i. +func (rb *reorderBuffer) insertSingle(src input, i int, info runeInfo) bool { + // insertOrder changes nbyte + pos := rb.nbyte + if !rb.insertOrdered(info) { + return false } + src.copySlice(rb.byte[pos:], i, i+int(info.size)) return true } @@ -182,8 +203,12 @@ const ( jamoLVTCount = 19 * 21 * 28 ) -// Caller must verify that len(b) >= 3. +const hangulUTF8Size = 3 + func isHangul(b []byte) bool { + if len(b) < hangulUTF8Size { + return false + } b0 := b[0] if b0 < hangulBase0 { return false @@ -202,8 +227,10 @@ func isHangul(b []byte) bool { return b1 == hangulEnd1 && b[2] < hangulEnd2 } -// Caller must verify that len(b) >= 3. func isHangulString(b string) bool { + if len(b) < hangulUTF8Size { + return false + } b0 := b[0] if b0 < hangulBase0 { return false @@ -234,6 +261,22 @@ func isHangulWithoutJamoT(b []byte) bool { return c < jamoLVTCount && c%jamoTCount == 0 } +// decomposeHangul writes the decomposed Hangul to buf and returns the number +// of bytes written. len(buf) should be at least 9. +func decomposeHangul(buf []byte, r rune) int { + const JamoUTF8Len = 3 + r -= hangulBase + x := r % jamoTCount + r /= jamoTCount + utf8.EncodeRune(buf, jamoLBase+r/jamoVCount) + utf8.EncodeRune(buf[JamoUTF8Len:], jamoVBase+r%jamoVCount) + if x != 0 { + utf8.EncodeRune(buf[2*JamoUTF8Len:], jamoTBase+x) + return 3 * JamoUTF8Len + } + return 2 * JamoUTF8Len +} + // decomposeHangul algorithmically decomposes a Hangul rune into // its Jamo components. // See http://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul. diff --git a/libgo/go/exp/norm/composition_test.go b/libgo/go/exp/norm/composition_test.go index e32380d7afa..9de9eacfd65 100644 --- a/libgo/go/exp/norm/composition_test.go +++ b/libgo/go/exp/norm/composition_test.go @@ -47,14 +47,14 @@ func runTests(t *testing.T, name string, fm Form, f insertFunc, tests []TestCase } } -func TestFlush(t *testing.T) { +type flushFunc func(rb *reorderBuffer) []byte + +func testFlush(t *testing.T, name string, fn flushFunc) { rb := reorderBuffer{} rb.init(NFC, nil) - out := make([]byte, 0) - - out = rb.flush(out) + out := fn(&rb) if len(out) != 0 { - t.Errorf("wrote bytes on flush of empty buffer. (len(out) = %d)", len(out)) + t.Errorf("%s: wrote bytes on flush of empty buffer. (len(out) = %d)", name, len(out)) } for _, r := range []rune("world!") { @@ -65,16 +65,32 @@ func TestFlush(t *testing.T) { out = rb.flush(out) want := "Hello world!" if string(out) != want { - t.Errorf(`output after flush was "%s"; want "%s"`, string(out), want) + t.Errorf(`%s: output after flush was "%s"; want "%s"`, name, string(out), want) } if rb.nrune != 0 { - t.Errorf("flush: non-null size of info buffer (rb.nrune == %d)", rb.nrune) + t.Errorf("%s: non-null size of info buffer (rb.nrune == %d)", name, rb.nrune) } if rb.nbyte != 0 { - t.Errorf("flush: non-null size of byte buffer (rb.nbyte == %d)", rb.nbyte) + t.Errorf("%s: non-null size of byte buffer (rb.nbyte == %d)", name, rb.nbyte) } } +func flushF(rb *reorderBuffer) []byte { + out := make([]byte, 0) + return rb.flush(out) +} + +func flushCopyF(rb *reorderBuffer) []byte { + out := make([]byte, MaxSegmentSize) + n := rb.flushCopy(out) + return out[:n] +} + +func TestFlush(t *testing.T) { + testFlush(t, "flush", flushF) + testFlush(t, "flushCopy", flushCopyF) +} + var insertTests = []TestCase{ {[]rune{'a'}, []rune{'a'}}, {[]rune{0x300}, []rune{0x300}}, diff --git a/libgo/go/exp/norm/input.go b/libgo/go/exp/norm/input.go index 5c0968ba58c..9c564d67718 100644 --- a/libgo/go/exp/norm/input.go +++ b/libgo/go/exp/norm/input.go @@ -7,7 +7,7 @@ package norm import "unicode/utf8" type input interface { - skipASCII(p int) int + skipASCII(p, max int) int skipNonStarter(p int) int appendSlice(buf []byte, s, e int) []byte copySlice(buf []byte, s, e int) @@ -18,8 +18,8 @@ type input interface { type inputString string -func (s inputString) skipASCII(p int) int { - for ; p < len(s) && s[p] < utf8.RuneSelf; p++ { +func (s inputString) skipASCII(p, max int) int { + for ; p < max && s[p] < utf8.RuneSelf; p++ { } return p } @@ -59,8 +59,8 @@ func (s inputString) hangul(p int) rune { type inputBytes []byte -func (s inputBytes) skipASCII(p int) int { - for ; p < len(s) && s[p] < utf8.RuneSelf; p++ { +func (s inputBytes) skipASCII(p, max int) int { + for ; p < max && s[p] < utf8.RuneSelf; p++ { } return p } diff --git a/libgo/go/exp/norm/iter.go b/libgo/go/exp/norm/iter.go new file mode 100644 index 00000000000..761ba90cdd4 --- /dev/null +++ b/libgo/go/exp/norm/iter.go @@ -0,0 +1,286 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +const MaxSegmentSize = maxByteBufferSize + +// An Iter iterates over a string or byte slice, while normalizing it +// to a given Form. +type Iter struct { + rb reorderBuffer + info runeInfo // first character saved from previous iteration + next iterFunc // implementation of next depends on form + + p int // current position in input source + outStart int // start of current segment in output buffer + inStart int // start of current segment in input source + maxp int // position in output buffer after which not to start a new segment + maxseg int // for tracking an excess of combining characters + + tccc uint8 + done bool +} + +type iterFunc func(*Iter, []byte) int + +// SetInput initializes i to iterate over src after normalizing it to Form f. +func (i *Iter) SetInput(f Form, src []byte) { + i.rb.init(f, src) + if i.rb.f.composing { + i.next = nextComposed + } else { + i.next = nextDecomposed + } + i.p = 0 + if i.done = len(src) == 0; !i.done { + i.info = i.rb.f.info(i.rb.src, i.p) + } +} + +// SetInputString initializes i to iterate over src after normalizing it to Form f. +func (i *Iter) SetInputString(f Form, src string) { + i.rb.initString(f, src) + if i.rb.f.composing { + i.next = nextComposed + } else { + i.next = nextDecomposed + } + i.p = 0 + if i.done = len(src) == 0; !i.done { + i.info = i.rb.f.info(i.rb.src, i.p) + } +} + +// Pos returns the byte position at which the next call to Next will commence processing. +func (i *Iter) Pos() int { + return i.p +} + +// Done returns true if there is no more input to process. +func (i *Iter) Done() bool { + return i.done +} + +// Next writes f(i.input[i.Pos():n]...) to buffer buf, where n is the +// largest boundary of i.input such that the result fits in buf. +// It returns the number of bytes written to buf. +// len(buf) should be at least MaxSegmentSize. +// Done must be false before calling Next. +func (i *Iter) Next(buf []byte) int { + return i.next(i, buf) +} + +func (i *Iter) initNext(outn, inStart int) { + i.outStart = 0 + i.inStart = inStart + i.maxp = outn - MaxSegmentSize + i.maxseg = MaxSegmentSize +} + +// setStart resets the start of the new segment to the given position. +// It returns true if there is not enough room for the new segment. +func (i *Iter) setStart(outp, inp int) bool { + if outp > i.maxp { + return true + } + i.outStart = outp + i.inStart = inp + i.maxseg = outp + MaxSegmentSize + return false +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// nextDecomposed is the implementation of Next for forms NFD and NFKD. +func nextDecomposed(i *Iter, out []byte) int { + var outp int + i.initNext(len(out), i.p) +doFast: + inCopyStart, outCopyStart := i.p, outp // invariant xCopyStart <= i.xStart + for { + if sz := int(i.info.size); sz <= 1 { + // ASCII or illegal byte. Either way, advance by 1. + i.p++ + outp++ + max := min(i.rb.nsrc, len(out)-outp+i.p) + if np := i.rb.src.skipASCII(i.p, max); np > i.p { + outp += np - i.p + i.p = np + if i.p >= i.rb.nsrc { + break + } + // ASCII may combine with consecutive runes. + if i.setStart(outp-1, i.p-1) { + i.p-- + outp-- + i.info.size = 1 + break + } + } + } else if d := i.info.decomposition(); d != nil { + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p) + p := outp + len(d) + if p > i.maxseg && i.setStart(outp, i.p) { + return outp + } + copy(out[outp:], d) + outp = p + i.p += sz + inCopyStart, outCopyStart = i.p, outp + } else if r := i.rb.src.hangul(i.p); r != 0 { + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p) + for { + outp += decomposeHangul(out[outp:], r) + i.p += hangulUTF8Size + if r = i.rb.src.hangul(i.p); r == 0 { + break + } + if i.setStart(outp, i.p) { + return outp + } + } + inCopyStart, outCopyStart = i.p, outp + } else { + p := outp + sz + if p > i.maxseg && i.setStart(outp, i.p) { + break + } + outp = p + i.p += sz + } + if i.p >= i.rb.nsrc { + break + } + prevCC := i.info.tccc + i.info = i.rb.f.info(i.rb.src, i.p) + if cc := i.info.ccc; cc == 0 { + if i.setStart(outp, i.p) { + break + } + } else if cc < prevCC { + goto doNorm + } + } + if inCopyStart != i.p { + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p) + } + i.done = i.p >= i.rb.nsrc + return outp +doNorm: + // Insert what we have decomposed so far in the reorderBuffer. + // As we will only reorder, there will always be enough room. + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p) + if !i.rb.insertDecomposed(out[i.outStart:outp]) { + // Start over to prevent decompositions from crossing segment boundaries. + // This is a rare occurance. + i.p = i.inStart + i.info = i.rb.f.info(i.rb.src, i.p) + } + outp = i.outStart + for { + if !i.rb.insert(i.rb.src, i.p, i.info) { + break + } + if i.p += int(i.info.size); i.p >= i.rb.nsrc { + outp += i.rb.flushCopy(out[outp:]) + i.done = true + return outp + } + i.info = i.rb.f.info(i.rb.src, i.p) + if i.info.ccc == 0 { + break + } + } + // new segment or too many combining characters: exit normalization + if outp += i.rb.flushCopy(out[outp:]); i.setStart(outp, i.p) { + return outp + } + goto doFast +} + +// nextComposed is the implementation of Next for forms NFC and NFKC. +func nextComposed(i *Iter, out []byte) int { + var outp int + i.initNext(len(out), i.p) +doFast: + inCopyStart, outCopyStart := i.p, outp // invariant xCopyStart <= i.xStart + var prevCC uint8 + for { + if !i.info.isYesC() { + goto doNorm + } + if cc := i.info.ccc; cc == 0 { + if i.setStart(outp, i.p) { + break + } + } else if cc < prevCC { + goto doNorm + } + prevCC = i.info.tccc + sz := int(i.info.size) + if sz == 0 { + sz = 1 // illegal rune: copy byte-by-byte + } + p := outp + sz + if p > i.maxseg && i.setStart(outp, i.p) { + break + } + outp = p + i.p += sz + max := min(i.rb.nsrc, len(out)-outp+i.p) + if np := i.rb.src.skipASCII(i.p, max); np > i.p { + outp += np - i.p + i.p = np + if i.p >= i.rb.nsrc { + break + } + // ASCII may combine with consecutive runes. + if i.setStart(outp-1, i.p-1) { + i.p-- + outp-- + i.info = runeInfo{size: 1} + break + } + } + if i.p >= i.rb.nsrc { + break + } + i.info = i.rb.f.info(i.rb.src, i.p) + } + if inCopyStart != i.p { + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p) + } + i.done = i.p >= i.rb.nsrc + return outp +doNorm: + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.inStart) + outp, i.p = i.outStart, i.inStart + i.info = i.rb.f.info(i.rb.src, i.p) + for { + if !i.rb.insert(i.rb.src, i.p, i.info) { + break + } + if i.p += int(i.info.size); i.p >= i.rb.nsrc { + i.rb.compose() + outp += i.rb.flushCopy(out[outp:]) + i.done = true + return outp + } + i.info = i.rb.f.info(i.rb.src, i.p) + if i.info.boundaryBefore() { + break + } + } + i.rb.compose() + if outp += i.rb.flushCopy(out[outp:]); i.setStart(outp, i.p) { + return outp + } + goto doFast +} diff --git a/libgo/go/exp/norm/iter_test.go b/libgo/go/exp/norm/iter_test.go new file mode 100644 index 00000000000..f6e8d817251 --- /dev/null +++ b/libgo/go/exp/norm/iter_test.go @@ -0,0 +1,186 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "strings" + "testing" +) + +var iterBufSizes = []int{ + MaxSegmentSize, + 1.5 * MaxSegmentSize, + 2 * MaxSegmentSize, + 3 * MaxSegmentSize, + 100 * MaxSegmentSize, +} + +func doIterNorm(f Form, buf []byte, s string) []byte { + acc := []byte{} + i := Iter{} + i.SetInputString(f, s) + for !i.Done() { + n := i.Next(buf) + acc = append(acc, buf[:n]...) + } + return acc +} + +func runIterTests(t *testing.T, name string, f Form, tests []AppendTest, norm bool) { + for i, test := range tests { + in := test.left + test.right + gold := test.out + if norm { + gold = string(f.AppendString(nil, test.out)) + } + for _, sz := range iterBufSizes { + buf := make([]byte, sz) + out := string(doIterNorm(f, buf, in)) + if len(out) != len(gold) { + const msg = "%s:%d:%d: length is %d; want %d" + t.Errorf(msg, name, i, sz, len(out), len(gold)) + } + if out != gold { + // Find first rune that differs and show context. + ir := []rune(out) + ig := []rune(gold) + for j := 0; j < len(ir) && j < len(ig); j++ { + if ir[j] == ig[j] { + continue + } + if j -= 3; j < 0 { + j = 0 + } + for e := j + 7; j < e && j < len(ir) && j < len(ig); j++ { + const msg = "%s:%d:%d: runeAt(%d) = %U; want %U" + t.Errorf(msg, name, i, sz, j, ir[j], ig[j]) + } + break + } + } + } + } +} + +func rep(r rune, n int) string { + return strings.Repeat(string(r), n) +} + +var iterTests = []AppendTest{ + {"", ascii, ascii}, + {"", txt_all, txt_all}, + {"", "a" + rep(0x0300, MaxSegmentSize/2), "a" + rep(0x0300, MaxSegmentSize/2)}, +} + +var iterTestsD = []AppendTest{ + { // segment overflow on unchanged character + "", + "a" + rep(0x0300, MaxSegmentSize/2) + "\u0316", + "a" + rep(0x0300, MaxSegmentSize/2-1) + "\u0316\u0300", + }, + { // segment overflow on unchanged character + start value + "", + "a" + rep(0x0300, MaxSegmentSize/2+maxCombiningChars+4) + "\u0316", + "a" + rep(0x0300, MaxSegmentSize/2+maxCombiningChars) + "\u0316" + rep(0x300, 4), + }, + { // segment overflow on decomposition + "", + "a" + rep(0x0300, MaxSegmentSize/2-1) + "\u0340", + "a" + rep(0x0300, MaxSegmentSize/2), + }, + { // segment overflow on decomposition + start value + "", + "a" + rep(0x0300, MaxSegmentSize/2-1) + "\u0340" + rep(0x300, maxCombiningChars+4) + "\u0320", + "a" + rep(0x0300, MaxSegmentSize/2-1) + rep(0x300, maxCombiningChars+1) + "\u0320" + rep(0x300, 4), + }, + { // start value after ASCII overflow + "", + rep('a', MaxSegmentSize) + rep(0x300, maxCombiningChars+2) + "\u0320", + rep('a', MaxSegmentSize) + rep(0x300, maxCombiningChars) + "\u0320\u0300\u0300", + }, + { // start value after Hangul overflow + "", + rep(0xAC00, MaxSegmentSize/6) + rep(0x300, maxCombiningChars+2) + "\u0320", + strings.Repeat("\u1100\u1161", MaxSegmentSize/6) + rep(0x300, maxCombiningChars-1) + "\u0320" + rep(0x300, 3), + }, + { // start value after cc=0 + "", + "您您" + rep(0x300, maxCombiningChars+4) + "\u0320", + "您您" + rep(0x300, maxCombiningChars) + "\u0320" + rep(0x300, 4), + }, + { // start value after normalization + "", + "\u0300\u0320a" + rep(0x300, maxCombiningChars+4) + "\u0320", + "\u0320\u0300a" + rep(0x300, maxCombiningChars) + "\u0320" + rep(0x300, 4), + }, +} + +var iterTestsC = []AppendTest{ + { // ordering of non-composing combining characters + "", + "\u0305\u0316", + "\u0316\u0305", + }, + { // segment overflow + "", + "a" + rep(0x0305, MaxSegmentSize/2+4) + "\u0316", + "a" + rep(0x0305, MaxSegmentSize/2-1) + "\u0316" + rep(0x305, 5), + }, +} + +func TestIterNextD(t *testing.T) { + runIterTests(t, "IterNextD1", NFKD, appendTests, true) + runIterTests(t, "IterNextD2", NFKD, iterTests, true) + runIterTests(t, "IterNextD3", NFKD, iterTestsD, false) +} + +func TestIterNextC(t *testing.T) { + runIterTests(t, "IterNextC1", NFKC, appendTests, true) + runIterTests(t, "IterNextC2", NFKC, iterTests, true) + runIterTests(t, "IterNextC3", NFKC, iterTestsC, false) +} + +type SegmentTest struct { + in string + out []string +} + +var segmentTests = []SegmentTest{ + {rep('a', MaxSegmentSize), []string{rep('a', MaxSegmentSize), ""}}, + {rep('a', MaxSegmentSize+2), []string{rep('a', MaxSegmentSize-1), "aaa", ""}}, + {rep('a', MaxSegmentSize) + "\u0300aa", []string{rep('a', MaxSegmentSize-1), "a\u0300", "aa", ""}}, +} + +// Note that, by design, segmentation is equal for composing and decomposing forms. +func TestIterSegmentation(t *testing.T) { + segmentTest(t, "SegmentTestD", NFD, segmentTests) + segmentTest(t, "SegmentTestC", NFC, segmentTests) +} + +func segmentTest(t *testing.T, name string, f Form, tests []SegmentTest) { + iter := Iter{} + for i, tt := range segmentTests { + buf := make([]byte, MaxSegmentSize) + iter.SetInputString(f, tt.in) + for j, seg := range tt.out { + if seg == "" { + if !iter.Done() { + n := iter.Next(buf) + res := string(buf[:n]) + t.Errorf(`%s:%d:%d: expected Done()==true, found segment "%s"`, name, i, j, res) + } + continue + } + if iter.Done() { + t.Errorf("%s:%d:%d: Done()==true, want false", name, i, j) + } + n := iter.Next(buf) + seg = f.String(seg) + if res := string(buf[:n]); res != seg { + t.Errorf(`%s:%d:%d" segment was "%s" (%d); want "%s" (%d)`, name, i, j, res, len(res), seg, len(seg)) + } + } + } +} diff --git a/libgo/go/exp/norm/normalize.go b/libgo/go/exp/norm/normalize.go index 030d900918e..b5cd44abfa0 100644 --- a/libgo/go/exp/norm/normalize.go +++ b/libgo/go/exp/norm/normalize.go @@ -243,7 +243,7 @@ func quickSpan(rb *reorderBuffer, i int) int { lastSegStart := i src, n := rb.src, rb.nsrc for i < n { - if j := src.skipASCII(i); i != j { + if j := src.skipASCII(i, n); i != j { i = j lastSegStart = i - 1 lastCC = 0 @@ -448,11 +448,16 @@ func decomposeToLastBoundary(rb *reorderBuffer, buf []byte) []byte { } // Check that decomposition doesn't result in overflow. if info.hasDecomposition() { - dcomp := info.decomposition() - for i := 0; i < len(dcomp); { - inf := rb.f.info(inputBytes(dcomp), i) - i += int(inf.size) + if isHangul(buf) { + i += int(info.size) n++ + } else { + dcomp := info.decomposition() + for i := 0; i < len(dcomp); { + inf := rb.f.info(inputBytes(dcomp), i) + i += int(inf.size) + n++ + } } } else { n++ diff --git a/libgo/go/exp/norm/normalize_test.go b/libgo/go/exp/norm/normalize_test.go index c7d5e08fca0..8b970598b4d 100644 --- a/libgo/go/exp/norm/normalize_test.go +++ b/libgo/go/exp/norm/normalize_test.go @@ -5,6 +5,7 @@ package norm import ( + "bytes" "strings" "testing" ) @@ -495,15 +496,40 @@ func TestAppend(t *testing.T) { runAppendTests(t, "TestString", NFKC, stringF, appendTests) } +func appendBench(f Form, in []byte) func() { + buf := make([]byte, 0, 4*len(in)) + return func() { + f.Append(buf, in...) + } +} + +func iterBench(f Form, in []byte) func() { + buf := make([]byte, 4*len(in)) + iter := Iter{} + return func() { + iter.SetInput(f, in) + for !iter.Done() { + iter.Next(buf) + } + } +} + +func appendBenchmarks(bm []func(), f Form, in []byte) []func() { + //bm = append(bm, appendBench(f, in)) + bm = append(bm, iterBench(f, in)) + return bm +} + func doFormBenchmark(b *testing.B, inf, f Form, s string) { b.StopTimer() in := inf.Bytes([]byte(s)) - buf := make([]byte, 2*len(in)) - b.SetBytes(int64(len(in))) + bm := appendBenchmarks(nil, f, in) + b.SetBytes(int64(len(in) * len(bm))) b.StartTimer() for i := 0; i < b.N; i++ { - buf = f.Append(buf[0:0], in...) - buf = buf[0:0] + for _, fn := range bm { + fn() + } } } @@ -549,17 +575,21 @@ func BenchmarkNormalizeHangulNFD2NFD(b *testing.B) { doFormBenchmark(b, NFD, NFD, txt_kr) } +var forms = []Form{NFC, NFD, NFKC, NFKD} + func doTextBenchmark(b *testing.B, s string) { b.StopTimer() - b.SetBytes(int64(len(s)) * 4) in := []byte(s) - var buf = make([]byte, 0, 2*len(in)) + bm := []func(){} + for _, f := range forms { + bm = appendBenchmarks(bm, f, in) + } + b.SetBytes(int64(len(s) * len(bm))) b.StartTimer() for i := 0; i < b.N; i++ { - NFC.Append(buf, in...) - NFD.Append(buf, in...) - NFKC.Append(buf, in...) - NFKD.Append(buf, in...) + for _, f := range bm { + f() + } } } @@ -584,6 +614,11 @@ func BenchmarkJapanese(b *testing.B) { func BenchmarkChinese(b *testing.B) { doTextBenchmark(b, txt_cn) } +func BenchmarkOverflow(b *testing.B) { + doTextBenchmark(b, overflow) +} + +var overflow = string(bytes.Repeat([]byte("\u035D"), 4096)) + "\u035B" // Tests sampled from the Canonical ordering tests (Part 2) of // http://unicode.org/Public/UNIDATA/NormalizationTest.txt diff --git a/libgo/go/exp/norm/normregtest.go b/libgo/go/exp/norm/normregtest.go index c2ab25bc99d..507de1ae834 100644 --- a/libgo/go/exp/norm/normregtest.go +++ b/libgo/go/exp/norm/normregtest.go @@ -220,6 +220,17 @@ func cmpIsNormal(t *Test, name string, f norm.Form, test string, result, want bo func doTest(t *Test, f norm.Form, gold, test string) { result := f.Bytes([]byte(test)) cmpResult(t, "Bytes", f, gold, test, string(result)) + sresult := f.String(test) + cmpResult(t, "String", f, gold, test, sresult) + buf := make([]byte, norm.MaxSegmentSize) + acc := []byte{} + i := norm.Iter{} + i.SetInputString(f, test) + for !i.Done() { + n := i.Next(buf) + acc = append(acc, buf[:n]...) + } + cmpResult(t, "Iter.Next", f, gold, test, string(acc)) for i := range test { out := f.Append(f.Bytes([]byte(test[:i])), []byte(test[i:])...) cmpResult(t, fmt.Sprintf(":Append:%d", i), f, gold, test, string(out)) diff --git a/libgo/go/exp/proxy/socks5.go b/libgo/go/exp/proxy/socks5.go index 466e135eb10..62fa5c9296d 100644 --- a/libgo/go/exp/proxy/socks5.go +++ b/libgo/go/exp/proxy/socks5.go @@ -98,9 +98,9 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) { buf = append(buf, socks5Version) if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { - buf = append(buf, 2, /* num auth methods */ socks5AuthNone, socks5AuthPassword) + buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword) } else { - buf = append(buf, 1, /* num auth methods */ socks5AuthNone) + buf = append(buf, 1 /* num auth methods */, socks5AuthNone) } if _, err = conn.Write(buf); err != nil { @@ -139,7 +139,7 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) { } buf = buf[:0] - buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */ ) + buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */) if ip := net.ParseIP(host); ip != nil { if len(ip) == 4 { diff --git a/libgo/go/exp/terminal/terminal.go b/libgo/go/exp/terminal/terminal.go index c3ba5bde2ee..c1ed0c0c443 100644 --- a/libgo/go/exp/terminal/terminal.go +++ b/libgo/go/exp/terminal/terminal.go @@ -389,12 +389,12 @@ func (t *Terminal) Write(buf []byte) (n int, err error) { // We have a prompt and possibly user input on the screen. We // have to clear it first. - t.move(0, /* up */ 0, /* down */ t.cursorX, /* left */ 0 /* right */ ) + t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */) t.cursorX = 0 t.clearLineToRight() for t.cursorY > 0 { - t.move(1, /* up */ 0, 0, 0) + t.move(1 /* up */, 0, 0, 0) t.cursorY-- t.clearLineToRight() } diff --git a/libgo/go/exp/winfsnotify/winfsnotify_test.go b/libgo/go/exp/winfsnotify/winfsnotify_test.go index 59ac1624a26..4a1929a8397 100644 --- a/libgo/go/exp/winfsnotify/winfsnotify_test.go +++ b/libgo/go/exp/winfsnotify/winfsnotify_test.go @@ -7,6 +7,7 @@ package winfsnotify import ( + "io/ioutil" "os" "testing" "time" @@ -115,7 +116,13 @@ func TestNotifyClose(t *testing.T) { t.Fatal("double Close() test failed: second Close() call didn't return") } - err := watcher.Watch("_test") + dir, err := ioutil.TempDir("", "wininotify") + if err != nil { + t.Fatalf("TempDir failed: %s", err) + } + defer os.RemoveAll(dir) + + err = watcher.Watch(dir) if err == nil { t.Fatal("expected error on Watch() after Close(), got nil") } |