diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-10-26 23:57:58 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-10-26 23:57:58 +0000 |
commit | fa5d125b5cfa5c935e46d27a2cbcd71ae37687ac (patch) | |
tree | 19d182df05ead7ff8ba7ee00a7d57555e1383fdf /libgo/go/exp | |
parent | e3d46e67996cf20ca3a75fccbb5a0007bfa3f992 (diff) | |
download | gcc-fa5d125b5cfa5c935e46d27a2cbcd71ae37687ac.tar.gz |
Update Go library to last weekly.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@180552 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/exp')
96 files changed, 21884 insertions, 8846 deletions
diff --git a/libgo/go/exp/ebnf/ebnf.go b/libgo/go/exp/ebnf/ebnf.go new file mode 100644 index 00000000000..2ec7f00800d --- /dev/null +++ b/libgo/go/exp/ebnf/ebnf.go @@ -0,0 +1,269 @@ +// Copyright 2009 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 ebnf is a library for EBNF grammars. The input is text ([]byte) +// satisfying the following grammar (represented itself in EBNF): +// +// Production = name "=" [ Expression ] "." . +// Expression = Alternative { "|" Alternative } . +// Alternative = Term { Term } . +// Term = name | token [ "…" token ] | Group | Option | Repetition . +// Group = "(" Expression ")" . +// Option = "[" Expression "]" . +// Repetition = "{" Expression "}" . +// +// A name is a Go identifier, a token is a Go string, and comments +// and white space follow the same rules as for the Go language. +// Production names starting with an uppercase Unicode letter denote +// non-terminal productions (i.e., productions which allow white-space +// and comments between tokens); all other production names denote +// lexical productions. +// +package ebnf + +import ( + "fmt" + "os" + "scanner" + "unicode" + "utf8" +) + +// ---------------------------------------------------------------------------- +// Error handling + +type errorList []os.Error + +func (list errorList) Error() os.Error { + if len(list) == 0 { + return nil + } + return list +} + +func (list errorList) String() string { + switch len(list) { + case 0: + return "no errors" + case 1: + return list[0].String() + } + return fmt.Sprintf("%s (and %d more errors)", list[0], len(list)-1) +} + +func newError(pos scanner.Position, msg string) os.Error { + return os.NewError(fmt.Sprintf("%s: %s", pos, msg)) +} + +// ---------------------------------------------------------------------------- +// Internal representation + +type ( + // An Expression node represents a production expression. + Expression interface { + // Pos is the position of the first character of the syntactic construct + Pos() scanner.Position + } + + // An Alternative node represents a non-empty list of alternative expressions. + Alternative []Expression // x | y | z + + // A Sequence node represents a non-empty list of sequential expressions. + Sequence []Expression // x y z + + // A Name node represents a production name. + Name struct { + StringPos scanner.Position + String string + } + + // A Token node represents a literal. + Token struct { + StringPos scanner.Position + String string + } + + // A List node represents a range of characters. + Range struct { + Begin, End *Token // begin ... end + } + + // A Group node represents a grouped expression. + Group struct { + Lparen scanner.Position + Body Expression // (body) + } + + // An Option node represents an optional expression. + Option struct { + Lbrack scanner.Position + Body Expression // [body] + } + + // A Repetition node represents a repeated expression. + Repetition struct { + Lbrace scanner.Position + Body Expression // {body} + } + + // A Production node represents an EBNF production. + Production struct { + Name *Name + Expr Expression + } + + // A Bad node stands for pieces of source code that lead to a parse error. + Bad struct { + TokPos scanner.Position + Error string // parser error message + } + + // A Grammar is a set of EBNF productions. The map + // is indexed by production name. + // + Grammar map[string]*Production +) + +func (x Alternative) Pos() scanner.Position { return x[0].Pos() } // the parser always generates non-empty Alternative +func (x Sequence) Pos() scanner.Position { return x[0].Pos() } // the parser always generates non-empty Sequences +func (x *Name) Pos() scanner.Position { return x.StringPos } +func (x *Token) Pos() scanner.Position { return x.StringPos } +func (x *Range) Pos() scanner.Position { return x.Begin.Pos() } +func (x *Group) Pos() scanner.Position { return x.Lparen } +func (x *Option) Pos() scanner.Position { return x.Lbrack } +func (x *Repetition) Pos() scanner.Position { return x.Lbrace } +func (x *Production) Pos() scanner.Position { return x.Name.Pos() } +func (x *Bad) Pos() scanner.Position { return x.TokPos } + +// ---------------------------------------------------------------------------- +// Grammar verification + +func isLexical(name string) bool { + ch, _ := utf8.DecodeRuneInString(name) + return !unicode.IsUpper(ch) +} + +type verifier struct { + errors errorList + worklist []*Production + reached Grammar // set of productions reached from (and including) the root production + grammar Grammar +} + +func (v *verifier) error(pos scanner.Position, msg string) { + v.errors = append(v.errors, newError(pos, msg)) +} + +func (v *verifier) push(prod *Production) { + name := prod.Name.String + if _, found := v.reached[name]; !found { + v.worklist = append(v.worklist, prod) + v.reached[name] = prod + } +} + +func (v *verifier) verifyChar(x *Token) int { + s := x.String + if utf8.RuneCountInString(s) != 1 { + v.error(x.Pos(), "single char expected, found "+s) + return 0 + } + ch, _ := utf8.DecodeRuneInString(s) + return ch +} + +func (v *verifier) verifyExpr(expr Expression, lexical bool) { + switch x := expr.(type) { + case nil: + // empty expression + case Alternative: + for _, e := range x { + v.verifyExpr(e, lexical) + } + case Sequence: + for _, e := range x { + v.verifyExpr(e, lexical) + } + case *Name: + // a production with this name must exist; + // add it to the worklist if not yet processed + if prod, found := v.grammar[x.String]; found { + v.push(prod) + } else { + v.error(x.Pos(), "missing production "+x.String) + } + // within a lexical production references + // to non-lexical productions are invalid + if lexical && !isLexical(x.String) { + v.error(x.Pos(), "reference to non-lexical production "+x.String) + } + case *Token: + // nothing to do for now + case *Range: + i := v.verifyChar(x.Begin) + j := v.verifyChar(x.End) + if i >= j { + v.error(x.Pos(), "decreasing character range") + } + case *Group: + v.verifyExpr(x.Body, lexical) + case *Option: + v.verifyExpr(x.Body, lexical) + case *Repetition: + v.verifyExpr(x.Body, lexical) + case *Bad: + v.error(x.Pos(), x.Error) + default: + panic(fmt.Sprintf("internal error: unexpected type %T", expr)) + } +} + +func (v *verifier) verify(grammar Grammar, start string) { + // find root production + root, found := grammar[start] + if !found { + var noPos scanner.Position + v.error(noPos, "no start production "+start) + return + } + + // initialize verifier + v.worklist = v.worklist[0:0] + v.reached = make(Grammar) + v.grammar = grammar + + // work through the worklist + v.push(root) + for { + n := len(v.worklist) - 1 + if n < 0 { + break + } + prod := v.worklist[n] + v.worklist = v.worklist[0:n] + v.verifyExpr(prod.Expr, isLexical(prod.Name.String)) + } + + // check if all productions were reached + if len(v.reached) < len(v.grammar) { + for name, prod := range v.grammar { + if _, found := v.reached[name]; !found { + v.error(prod.Pos(), name+" is unreachable") + } + } + } +} + +// Verify checks that: +// - all productions used are defined +// - all productions defined are used when beginning at start +// - lexical productions refer only to other lexical productions +// +// Position information is interpreted relative to the file set fset. +// +func Verify(grammar Grammar, start string) os.Error { + var v verifier + v.verify(grammar, start) + return v.errors.Error() +} diff --git a/libgo/go/exp/ebnf/ebnf_test.go b/libgo/go/exp/ebnf/ebnf_test.go new file mode 100644 index 00000000000..8cfd6b9c370 --- /dev/null +++ b/libgo/go/exp/ebnf/ebnf_test.go @@ -0,0 +1,71 @@ +// Copyright 2009 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 ebnf + +import ( + "bytes" + "testing" +) + +var goodGrammars = []string{ + `Program = .`, + + `Program = foo . + foo = "foo" .`, + + `Program = "a" | "b" "c" .`, + + `Program = "a" … "z" .`, + + `Program = Song . + Song = { Note } . + Note = Do | (Re | Mi | Fa | So | La) | Ti . + Do = "c" . + Re = "d" . + Mi = "e" . + Fa = "f" . + So = "g" . + La = "a" . + Ti = ti . + ti = "b" .`, +} + +var badGrammars = []string{ + `Program = | .`, + `Program = | b .`, + `Program = a … b .`, + `Program = "a" … .`, + `Program = … "b" .`, + `Program = () .`, + `Program = [] .`, + `Program = {} .`, +} + +func checkGood(t *testing.T, src string) { + grammar, err := Parse("", bytes.NewBuffer([]byte(src))) + if err != nil { + t.Errorf("Parse(%s) failed: %v", src, err) + return + } + if err = Verify(grammar, "Program"); err != nil { + t.Errorf("Verify(%s) failed: %v", src, err) + } +} + +func checkBad(t *testing.T, src string) { + _, err := Parse("", bytes.NewBuffer([]byte(src))) + if err == nil { + t.Errorf("Parse(%s) should have failed", src) + } +} + +func TestGrammars(t *testing.T) { + for _, src := range goodGrammars { + checkGood(t, src) + } + for _, src := range badGrammars { + checkBad(t, src) + } +} diff --git a/libgo/go/exp/ebnf/parser.go b/libgo/go/exp/ebnf/parser.go new file mode 100644 index 00000000000..2dbbefb7519 --- /dev/null +++ b/libgo/go/exp/ebnf/parser.go @@ -0,0 +1,191 @@ +// Copyright 2009 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 ebnf + +import ( + "io" + "os" + "scanner" + "strconv" +) + +type parser struct { + errors errorList + scanner scanner.Scanner + pos scanner.Position // token position + tok int // one token look-ahead + lit string // token literal +} + +func (p *parser) next() { + p.tok = p.scanner.Scan() + p.pos = p.scanner.Position + p.lit = p.scanner.TokenText() +} + +func (p *parser) error(pos scanner.Position, msg string) { + p.errors = append(p.errors, newError(pos, msg)) +} + +func (p *parser) errorExpected(pos scanner.Position, msg string) { + msg = `expected "` + msg + `"` + if pos.Offset == p.pos.Offset { + // the error happened at the current position; + // make the error message more specific + msg += ", found " + scanner.TokenString(p.tok) + if p.tok < 0 { + msg += " " + p.lit + } + } + p.error(pos, msg) +} + +func (p *parser) expect(tok int) scanner.Position { + pos := p.pos + if p.tok != tok { + p.errorExpected(pos, scanner.TokenString(tok)) + } + p.next() // make progress in any case + return pos +} + +func (p *parser) parseIdentifier() *Name { + pos := p.pos + name := p.lit + p.expect(scanner.Ident) + return &Name{pos, name} +} + +func (p *parser) parseToken() *Token { + pos := p.pos + value := "" + if p.tok == scanner.String { + value, _ = strconv.Unquote(p.lit) + // Unquote may fail with an error, but only if the scanner found + // an illegal string in the first place. In this case the error + // has already been reported. + p.next() + } else { + p.expect(scanner.String) + } + return &Token{pos, value} +} + +// ParseTerm returns nil if no term was found. +func (p *parser) parseTerm() (x Expression) { + pos := p.pos + + switch p.tok { + case scanner.Ident: + x = p.parseIdentifier() + + case scanner.String: + tok := p.parseToken() + x = tok + const ellipsis = '…' // U+2026, the horizontal ellipsis character + if p.tok == ellipsis { + p.next() + x = &Range{tok, p.parseToken()} + } + + case '(': + p.next() + x = &Group{pos, p.parseExpression()} + p.expect(')') + + case '[': + p.next() + x = &Option{pos, p.parseExpression()} + p.expect(']') + + case '{': + p.next() + x = &Repetition{pos, p.parseExpression()} + p.expect('}') + } + + return x +} + +func (p *parser) parseSequence() Expression { + var list Sequence + + for x := p.parseTerm(); x != nil; x = p.parseTerm() { + list = append(list, x) + } + + // no need for a sequence if list.Len() < 2 + switch len(list) { + case 0: + p.errorExpected(p.pos, "term") + return &Bad{p.pos, "term expected"} + case 1: + return list[0] + } + + return list +} + +func (p *parser) parseExpression() Expression { + var list Alternative + + for { + list = append(list, p.parseSequence()) + if p.tok != '|' { + break + } + p.next() + } + // len(list) > 0 + + // no need for an Alternative node if list.Len() < 2 + if len(list) == 1 { + return list[0] + } + + return list +} + +func (p *parser) parseProduction() *Production { + name := p.parseIdentifier() + p.expect('=') + var expr Expression + if p.tok != '.' { + expr = p.parseExpression() + } + p.expect('.') + return &Production{name, expr} +} + +func (p *parser) parse(filename string, src io.Reader) Grammar { + p.scanner.Init(src) + p.scanner.Filename = filename + p.next() // initializes pos, tok, lit + + grammar := make(Grammar) + for p.tok != scanner.EOF { + prod := p.parseProduction() + name := prod.Name.String + if _, found := grammar[name]; !found { + grammar[name] = prod + } else { + p.error(prod.Pos(), name+" declared already") + } + } + + return grammar +} + +// Parse parses a set of EBNF productions from source src. +// It returns a set of productions. Errors are reported +// for incorrect syntax and if a production is declared +// more than once; the filename is used only for error +// positions. +// +func Parse(filename string, src io.Reader) (Grammar, os.Error) { + var p parser + grammar := p.parse(filename, src) + return grammar, p.errors.Error() +} diff --git a/libgo/go/exp/ebnflint/doc.go b/libgo/go/exp/ebnflint/doc.go new file mode 100644 index 00000000000..f35976eea73 --- /dev/null +++ b/libgo/go/exp/ebnflint/doc.go @@ -0,0 +1,22 @@ +// Copyright 2009 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. + +/* + +Ebnflint verifies that EBNF productions are consistent and gramatically correct. +It reads them from an HTML document such as the Go specification. + +Grammar productions are grouped in boxes demarcated by the HTML elements + <pre class="ebnf"> + </pre> + + +Usage: + ebnflint [--start production] [file] + +The --start flag specifies the name of the start production for +the grammar; it defaults to "Start". + +*/ +package documentation diff --git a/libgo/go/exp/ebnflint/ebnflint.go b/libgo/go/exp/ebnflint/ebnflint.go new file mode 100644 index 00000000000..c827716c44c --- /dev/null +++ b/libgo/go/exp/ebnflint/ebnflint.go @@ -0,0 +1,109 @@ +// Copyright 2009 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 main + +import ( + "bytes" + "exp/ebnf" + "flag" + "fmt" + "go/scanner" + "go/token" + "io/ioutil" + "os" + "path/filepath" +) + +var fset = token.NewFileSet() +var start = flag.String("start", "Start", "name of start production") + +func usage() { + fmt.Fprintf(os.Stderr, "usage: ebnflint [flags] [filename]\n") + flag.PrintDefaults() + os.Exit(1) +} + +// Markers around EBNF sections in .html files +var ( + open = []byte(`<pre class="ebnf">`) + close = []byte(`</pre>`) +) + +func report(err os.Error) { + scanner.PrintError(os.Stderr, err) + os.Exit(1) +} + +func extractEBNF(src []byte) []byte { + var buf bytes.Buffer + + for { + // i = beginning of EBNF text + i := bytes.Index(src, open) + if i < 0 { + break // no EBNF found - we are done + } + i += len(open) + + // write as many newlines as found in the excluded text + // to maintain correct line numbers in error messages + for _, ch := range src[0:i] { + if ch == '\n' { + buf.WriteByte('\n') + } + } + + // j = end of EBNF text (or end of source) + j := bytes.Index(src[i:], close) // close marker + if j < 0 { + j = len(src) - i + } + j += i + + // copy EBNF text + buf.Write(src[i:j]) + + // advance + src = src[j:] + } + + return buf.Bytes() +} + +func main() { + flag.Parse() + + var ( + filename string + src []byte + err os.Error + ) + switch flag.NArg() { + case 0: + filename = "<stdin>" + src, err = ioutil.ReadAll(os.Stdin) + case 1: + filename = flag.Arg(0) + src, err = ioutil.ReadFile(filename) + default: + usage() + } + if err != nil { + report(err) + } + + if filepath.Ext(filename) == ".html" || bytes.Index(src, open) >= 0 { + src = extractEBNF(src) + } + + grammar, err := ebnf.Parse(filename, bytes.NewBuffer(src)) + if err != nil { + report(err) + } + + if err = ebnf.Verify(grammar, *start); err != nil { + report(err) + } +} diff --git a/libgo/go/exp/gotype/doc.go b/libgo/go/exp/gotype/doc.go new file mode 100644 index 00000000000..1aa0faa751a --- /dev/null +++ b/libgo/go/exp/gotype/doc.go @@ -0,0 +1,61 @@ +// 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. + +/* +The gotype command does syntactic and semantic analysis of Go files +and packages similar to the analysis performed by the front-end of +a Go compiler. Errors are reported if the analysis fails; otherwise +gotype is quiet (unless -v is set). + +Without a list of paths, gotype processes the standard input, which must +be the source of a single package file. + +Given a list of file names, each file must be a source file belonging to +the same package unless the package name is explicitly specified with the +-p flag. + +Given a directory name, gotype collects all .go files in the directory +and processes them as if they were provided as an explicit list of file +names. Each directory is processed independently. Files starting with . +or not ending in .go are ignored. + +Usage: + gotype [flags] [path ...] + +The flags are: + -e + Print all (including spurious) errors. + -p pkgName + Process only those files in package pkgName. + -r + Recursively process subdirectories. + -v + Verbose mode. + +Debugging flags: + -ast + Print AST (disables concurrent parsing). + -trace + Print parse trace (disables concurrent parsing). + + +Examples + +To check the files file.go, old.saved, and .ignored: + + gotype file.go old.saved .ignored + +To check all .go files belonging to package main in the current directory +and recursively in all subdirectories: + + gotype -p main -r . + +To verify the output of a pipe: + + echo "package foo" | gotype + +*/ +package documentation + +// BUG(gri): At the moment, only single-file scope analysis is performed. diff --git a/libgo/go/exp/gotype/gotype.go b/libgo/go/exp/gotype/gotype.go new file mode 100644 index 00000000000..91992130077 --- /dev/null +++ b/libgo/go/exp/gotype/gotype.go @@ -0,0 +1,192 @@ +// 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 main + +import ( + "exp/types" + "flag" + "fmt" + "go/ast" + "go/parser" + "go/scanner" + "go/token" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +var ( + // main operation modes + pkgName = flag.String("p", "", "process only those files in package pkgName") + recursive = flag.Bool("r", false, "recursively process subdirectories") + verbose = flag.Bool("v", false, "verbose mode") + allErrors = flag.Bool("e", false, "print all (including spurious) errors") + + // debugging support + printTrace = flag.Bool("trace", false, "print parse trace") + printAST = flag.Bool("ast", false, "print AST") +) + +var exitCode = 0 + +func usage() { + fmt.Fprintf(os.Stderr, "usage: gotype [flags] [path ...]\n") + flag.PrintDefaults() + os.Exit(2) +} + +func report(err os.Error) { + scanner.PrintError(os.Stderr, err) + exitCode = 2 +} + +// parse returns the AST for the Go source src. +// The filename is for error reporting only. +// The result is nil if there were errors or if +// the file does not belong to the -p package. +func parse(fset *token.FileSet, filename string, src []byte) *ast.File { + if *verbose { + fmt.Println(filename) + } + + // ignore files with different package name + if *pkgName != "" { + file, err := parser.ParseFile(fset, filename, src, parser.PackageClauseOnly) + if err != nil { + report(err) + return nil + } + if file.Name.Name != *pkgName { + if *verbose { + fmt.Printf("\tignored (package %s)\n", file.Name.Name) + } + return nil + } + } + + // parse entire file + mode := parser.DeclarationErrors + if *allErrors { + mode |= parser.SpuriousErrors + } + if *printTrace { + mode |= parser.Trace + } + file, err := parser.ParseFile(fset, filename, src, mode) + if err != nil { + report(err) + return nil + } + if *printAST { + ast.Print(fset, file) + } + + return file +} + +func parseStdin(fset *token.FileSet) (files map[string]*ast.File) { + files = make(map[string]*ast.File) + src, err := ioutil.ReadAll(os.Stdin) + if err != nil { + report(err) + return + } + const filename = "<standard input>" + if file := parse(fset, filename, src); file != nil { + files[filename] = file + } + return +} + +func parseFiles(fset *token.FileSet, filenames []string) (files map[string]*ast.File) { + files = make(map[string]*ast.File) + for _, filename := range filenames { + src, err := ioutil.ReadFile(filename) + if err != nil { + report(err) + continue + } + if file := parse(fset, filename, src); file != nil { + if files[filename] != nil { + report(os.NewError(fmt.Sprintf("%q: duplicate file", filename))) + continue + } + files[filename] = file + } + } + return +} + +func isGoFilename(filename string) bool { + // ignore non-Go files + return !strings.HasPrefix(filename, ".") && strings.HasSuffix(filename, ".go") +} + +func processDirectory(dirname string) { + f, err := os.Open(dirname) + if err != nil { + report(err) + return + } + filenames, err := f.Readdirnames(-1) + f.Close() + if err != nil { + report(err) + // continue since filenames may not be empty + } + for i, filename := range filenames { + filenames[i] = filepath.Join(dirname, filename) + } + processFiles(filenames, false) +} + +func processFiles(filenames []string, allFiles bool) { + i := 0 + for _, filename := range filenames { + switch info, err := os.Stat(filename); { + case err != nil: + report(err) + case info.IsRegular(): + if allFiles || isGoFilename(info.Name) { + filenames[i] = filename + i++ + } + case info.IsDirectory(): + if allFiles || *recursive { + processDirectory(filename) + } + } + } + fset := token.NewFileSet() + processPackage(fset, parseFiles(fset, filenames[0:i])) +} + +func processPackage(fset *token.FileSet, files map[string]*ast.File) { + // make a package (resolve all identifiers) + pkg, err := ast.NewPackage(fset, files, types.GcImporter, types.Universe) + if err != nil { + report(err) + return + } + _, err = types.Check(fset, pkg) + if err != nil { + report(err) + } +} + +func main() { + flag.Usage = usage + flag.Parse() + + if flag.NArg() == 0 { + fset := token.NewFileSet() + processPackage(fset, parseStdin(fset)) + } else { + processFiles(flag.Args(), true) + } + + os.Exit(exitCode) +} diff --git a/libgo/go/exp/gotype/gotype_test.go b/libgo/go/exp/gotype/gotype_test.go new file mode 100644 index 00000000000..8732d4c5aa8 --- /dev/null +++ b/libgo/go/exp/gotype/gotype_test.go @@ -0,0 +1,49 @@ +// 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 main + +import ( + "path/filepath" + "runtime" + "testing" +) + +func runTest(t *testing.T, path, pkg string) { + exitCode = 0 + *pkgName = pkg + *recursive = false + + if pkg == "" { + processFiles([]string{path}, true) + } else { + processDirectory(path) + } + + if exitCode != 0 { + t.Errorf("processing %s failed: exitCode = %d", path, exitCode) + } +} + +var tests = []struct { + path string + pkg string +}{ + // individual files + {"testdata/test1.go", ""}, + + // directories + {filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"}, + {filepath.Join(runtime.GOROOT(), "src/pkg/go/doc"), "doc"}, + {filepath.Join(runtime.GOROOT(), "src/pkg/go/token"), "scanner"}, + {filepath.Join(runtime.GOROOT(), "src/pkg/go/scanner"), "scanner"}, + {filepath.Join(runtime.GOROOT(), "src/pkg/go/parser"), "parser"}, + {filepath.Join(runtime.GOROOT(), "src/pkg/exp/types"), "types"}, +} + +func Test(t *testing.T) { + for _, test := range tests { + runTest(t, test.path, test.pkg) + } +} diff --git a/libgo/go/exp/gotype/testdata/test1.go b/libgo/go/exp/gotype/testdata/test1.go new file mode 100644 index 00000000000..a3298e6e5fe --- /dev/null +++ b/libgo/go/exp/gotype/testdata/test1.go @@ -0,0 +1,23 @@ +package p + +func _() { + // the scope of a local type declaration starts immediately after the type name + type T struct{ _ *T } +} + +func _(x interface{}) { + // the variable defined by a TypeSwitchGuard is declared in each TypeCaseClause + switch t := x.(type) { + case int: + _ = t + case float32: + _ = t + default: + _ = t + } + + // the variable defined by a TypeSwitchGuard must not conflict with other + // variables declared in the initial simple statement + switch t := 0; t := x.(type) { + } +} diff --git a/libgo/go/exp/gui/x11/conn.go b/libgo/go/exp/gui/x11/conn.go index 98c65b95fae..bf94bcaabb4 100644 --- a/libgo/go/exp/gui/x11/conn.go +++ b/libgo/go/exp/gui/x11/conn.go @@ -621,7 +621,7 @@ func NewWindowDisplay(display string) (gui.Window, os.Error) { return nil, err } - c.img = image.NewRGBA(windowWidth, windowHeight) + c.img = image.NewRGBA(image.Rect(0, 0, windowWidth, windowHeight)) c.eventc = make(chan interface{}, 16) c.flush = make(chan bool, 1) go c.readSocket() diff --git a/libgo/go/exp/norm/composition.go b/libgo/go/exp/norm/composition.go index b2d2abaf63b..1d722230d6f 100644 --- a/libgo/go/exp/norm/composition.go +++ b/libgo/go/exp/norm/composition.go @@ -7,27 +7,46 @@ package norm import "utf8" const ( - maxCombiningChars = 30 + 2 // +2 to hold CGJ and Hangul overflow. + maxCombiningChars = 30 + maxBufferSize = maxCombiningChars + 2 // +1 to hold starter +1 to hold CGJ maxBackRunes = maxCombiningChars - 1 maxNFCExpansion = 3 // NFC(0x1D160) maxNFKCExpansion = 18 // NFKC(0xFDFA) - maxRuneSizeInDecomp = 4 - // Need to multiply by 2 as we don't reuse byte buffer space for recombining. - maxByteBufferSize = 2 * maxRuneSizeInDecomp * maxCombiningChars // 256 + maxByteBufferSize = utf8.UTFMax * maxBufferSize // 128 ) // reorderBuffer is used to normalize a single segment. Characters inserted with -// insert() are decomposed and reordered based on CCC. The compose() method can +// insert are decomposed and reordered based on CCC. The compose method can // be used to recombine characters. Note that the byte buffer does not hold // the UTF-8 characters in order. Only the rune array is maintained in sorted -// order. flush() writes the resulting segment to a byte array. +// order. flush writes the resulting segment to a byte array. type reorderBuffer struct { - rune [maxCombiningChars]runeInfo // Per character info. - byte [maxByteBufferSize]byte // UTF-8 buffer. Referenced by runeInfo.pos. - nrune int // Number of runeInfos. - nbyte uint8 // Number or bytes. + rune [maxBufferSize]runeInfo // Per character info. + byte [maxByteBufferSize]byte // UTF-8 buffer. Referenced by runeInfo.pos. + nrune int // Number of runeInfos. + nbyte uint8 // Number or bytes. f formInfo + + src input + nsrc int + srcBytes inputBytes + srcString inputString + tmpBytes inputBytes +} + +func (rb *reorderBuffer) init(f Form, src []byte) { + rb.f = *formTable[f] + rb.srcBytes = inputBytes(src) + rb.src = &rb.srcBytes + rb.nsrc = len(src) +} + +func (rb *reorderBuffer) initString(f Form, src string) { + rb.f = *formTable[f] + rb.srcString = inputString(src) + rb.src = &rb.srcString + rb.nsrc = len(src) } // reset discards all characters from the buffer. @@ -49,10 +68,10 @@ func (rb *reorderBuffer) flush(out []byte) []byte { // 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. +// It is used internally by insert and insertString only. func (rb *reorderBuffer) insertOrdered(info runeInfo) bool { n := rb.nrune - if n >= maxCombiningChars { + if n >= maxCombiningChars+1 { return false } b := rb.rune[:] @@ -68,7 +87,7 @@ func (rb *reorderBuffer) insertOrdered(info runeInfo) bool { } rb.nrune += 1 pos := uint8(rb.nbyte) - rb.nbyte += info.size + rb.nbyte += utf8.UTFMax info.pos = pos b[n] = info return true @@ -76,53 +95,32 @@ 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 []byte, info runeInfo) bool { - if info.size == 3 && isHangul(src) { - rune, _ := utf8.DecodeRune(src) - return rb.decomposeHangul(uint32(rune)) - } - pos := rb.nbyte - if info.flags.hasDecomposition() { - dcomp := rb.f.decompose(src) - for i := 0; i < len(dcomp); i += int(info.size) { - info = rb.f.info(dcomp[i:]) - if !rb.insertOrdered(info) { - return false - } - } - copy(rb.byte[pos:], dcomp) - } else { - if !rb.insertOrdered(info) { - return false +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(uint32(rune)) } - copy(rb.byte[pos:], src[:info.size]) } - return true -} - -// insertString 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) insertString(src string, info runeInfo) bool { - if info.size == 3 && isHangulString(src) { - rune, _ := utf8.DecodeRuneInString(src) - return rb.decomposeHangul(uint32(rune)) - } - pos := rb.nbyte - dcomp := rb.f.decomposeString(src) - dn := len(dcomp) - if dn != 0 { - for i := 0; i < dn; i += int(info.size) { - info = rb.f.info(dcomp[i:]) + if info.flags.hasDecomposition() { + dcomp := rb.f.decompose(src, i) + 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 } - copy(rb.byte[pos:], dcomp) } else { + // insertOrder changes nbyte + pos := rb.nbyte if !rb.insertOrdered(info) { return false } - copy(rb.byte[pos:], src[:info.size]) + src.copySlice(rb.byte[pos:], i, i+int(info.size)) } return true } @@ -131,17 +129,16 @@ func (rb *reorderBuffer) insertString(src string, info runeInfo) bool { func (rb *reorderBuffer) appendRune(rune uint32) { bn := rb.nbyte sz := utf8.EncodeRune(rb.byte[bn:], int(rune)) - rb.nbyte += uint8(sz) + rb.nbyte += utf8.UTFMax rb.rune[rb.nrune] = runeInfo{bn, uint8(sz), 0, 0} rb.nrune++ } // assignRune sets a rune at position pos. It is used for Hangul and recomposition. func (rb *reorderBuffer) assignRune(pos int, rune uint32) { - bn := rb.nbyte + bn := rb.rune[pos].pos sz := utf8.EncodeRune(rb.byte[bn:], int(rune)) rb.rune[pos] = runeInfo{bn, uint8(sz), 0, 0} - rb.nbyte += uint8(sz) } // runeAt returns the rune at position n. It is used for Hangul and recomposition. @@ -259,11 +256,10 @@ func (rb *reorderBuffer) decomposeHangul(rune uint32) bool { // combineHangul algorithmically combines Jamo character components into Hangul. // See http://unicode.org/reports/tr15/#Hangul for details on combining Hangul. -func (rb *reorderBuffer) combineHangul() { - k := 1 +func (rb *reorderBuffer) combineHangul(s, i, k int) { b := rb.rune[:] bn := rb.nrune - for s, i := 0, 1; i < bn; i++ { + for ; i < bn; i++ { cccB := b[k-1].ccc cccC := b[i].ccc if cccB == 0 { @@ -305,14 +301,17 @@ func (rb *reorderBuffer) compose() { // blocked from S if and only if there is some character B between S // and C, and either B is a starter or it has the same or higher // combining class as C." + bn := rb.nrune + if bn == 0 { + return + } k := 1 b := rb.rune[:] - bn := rb.nrune for s, i := 0, 1; i < bn; i++ { if isJamoVT(rb.bytesAt(i)) { // Redo from start in Hangul mode. Necessary to support // U+320E..U+321E in NFKC mode. - rb.combineHangul() + rb.combineHangul(s, i, k) return } ii := b[i] diff --git a/libgo/go/exp/norm/composition_test.go b/libgo/go/exp/norm/composition_test.go index 195a0c1e8e9..ce9caaff160 100644 --- a/libgo/go/exp/norm/composition_test.go +++ b/libgo/go/exp/norm/composition_test.go @@ -15,21 +15,19 @@ type TestCase struct { type insertFunc func(rb *reorderBuffer, rune int) bool func insert(rb *reorderBuffer, rune int) bool { - b := []byte(string(rune)) - return rb.insert(b, rb.f.info(b)) + src := inputString(string(rune)) + return rb.insert(src, 0, rb.f.info(src, 0)) } -func insertString(rb *reorderBuffer, rune int) bool { - s := string(rune) - return rb.insertString(s, rb.f.infoString(s)) -} - -func runTests(t *testing.T, name string, rb *reorderBuffer, f insertFunc, tests []TestCase) { +func runTests(t *testing.T, name string, fm Form, f insertFunc, tests []TestCase) { + rb := reorderBuffer{} + rb.init(fm, nil) for i, test := range tests { rb.reset() for j, rune := range test.in { b := []byte(string(rune)) - if !rb.insert(b, rb.f.info(b)) { + src := inputBytes(b) + if !rb.insert(src, 0, rb.f.info(src, 0)) { t.Errorf("%s:%d: insert failed for rune %d", name, i, j) } } @@ -50,7 +48,8 @@ func runTests(t *testing.T, name string, rb *reorderBuffer, f insertFunc, tests } func TestFlush(t *testing.T) { - rb := &reorderBuffer{f: *formTable[NFC]} + rb := reorderBuffer{} + rb.init(NFC, nil) out := make([]byte, 0) out = rb.flush(out) @@ -59,7 +58,7 @@ func TestFlush(t *testing.T) { } for _, r := range []int("world!") { - insert(rb, r) + insert(&rb, r) } out = []byte("Hello ") @@ -88,13 +87,7 @@ var insertTests = []TestCase{ } func TestInsert(t *testing.T) { - rb := &reorderBuffer{f: *formTable[NFD]} - runTests(t, "TestInsert", rb, insert, insertTests) -} - -func TestInsertString(t *testing.T) { - rb := &reorderBuffer{f: *formTable[NFD]} - runTests(t, "TestInsertString", rb, insertString, insertTests) + runTests(t, "TestInsert", NFD, insert, insertTests) } var decompositionNFDTest = []TestCase{ @@ -113,11 +106,8 @@ var decompositionNFKDTest = []TestCase{ } func TestDecomposition(t *testing.T) { - rb := &reorderBuffer{} - rb.f = *formTable[NFD] - runTests(t, "TestDecompositionNFD", rb, insert, decompositionNFDTest) - rb.f = *formTable[NFKD] - runTests(t, "TestDecompositionNFKD", rb, insert, decompositionNFKDTest) + runTests(t, "TestDecompositionNFD", NFD, insert, decompositionNFDTest) + runTests(t, "TestDecompositionNFKD", NFKD, insert, decompositionNFKDTest) } var compositionTest = []TestCase{ @@ -133,6 +123,5 @@ var compositionTest = []TestCase{ } func TestComposition(t *testing.T) { - rb := &reorderBuffer{f: *formTable[NFC]} - runTests(t, "TestComposition", rb, insert, compositionTest) + runTests(t, "TestComposition", NFC, insert, compositionTest) } diff --git a/libgo/go/exp/norm/forminfo.go b/libgo/go/exp/norm/forminfo.go index ee3edb8ea7d..d06a00602f8 100644 --- a/libgo/go/exp/norm/forminfo.go +++ b/libgo/go/exp/norm/forminfo.go @@ -15,10 +15,8 @@ type runeInfo struct { // functions dispatchable per form type boundaryFunc func(f *formInfo, info runeInfo) bool -type lookupFunc func(b []byte) runeInfo -type lookupFuncString func(s string) runeInfo -type decompFunc func(b []byte) []byte -type decompFuncString func(s string) []byte +type lookupFunc func(b input, i int) runeInfo +type decompFunc func(b input, i int) []byte // formInfo holds Form-specific functions and tables. type formInfo struct { @@ -26,12 +24,10 @@ type formInfo struct { composing, compatibility bool // form type - decompose decompFunc - decomposeString decompFuncString - info lookupFunc - infoString lookupFuncString - boundaryBefore boundaryFunc - boundaryAfter boundaryFunc + decompose decompFunc + info lookupFunc + boundaryBefore boundaryFunc + boundaryAfter boundaryFunc } var formTable []*formInfo @@ -46,14 +42,10 @@ func init() { if Form(i) == NFKD || Form(i) == NFKC { f.compatibility = true f.decompose = decomposeNFKC - f.decomposeString = decomposeStringNFKC f.info = lookupInfoNFKC - f.infoString = lookupInfoStringNFKC } else { f.decompose = decomposeNFC - f.decomposeString = decomposeStringNFC f.info = lookupInfoNFC - f.infoString = lookupInfoStringNFC } if Form(i) == NFC || Form(i) == NFKC { f.composing = true @@ -77,7 +69,7 @@ func decompBoundary(f *formInfo, info runeInfo) bool { } func compBoundaryBefore(f *formInfo, info runeInfo) bool { - if info.ccc == 0 && info.flags.isYesC() { + if info.ccc == 0 && !info.flags.combinesBackward() { return true } // We assume that the CCC of the first character in a decomposition @@ -89,9 +81,7 @@ func compBoundaryBefore(f *formInfo, info runeInfo) bool { func compBoundaryAfter(f *formInfo, info runeInfo) bool { // This misses values where the last char in a decomposition is a // boundary such as Hangul with JamoT. - // TODO(mpvl): verify this does not lead to segments that do - // not fit in the reorderBuffer. - return info.flags.isInert() + return info.isInert() } // We pack quick check data in 4 bits: @@ -110,41 +100,30 @@ func (i qcInfo) isNoC() bool { return i&0x6 == 0x2 } func (i qcInfo) isMaybe() bool { return i&0x4 != 0 } func (i qcInfo) isYesD() bool { return i&0x1 == 0 } func (i qcInfo) isNoD() bool { return i&0x1 != 0 } -func (i qcInfo) isInert() bool { return i&0xf == 0 } func (i qcInfo) combinesForward() bool { return i&0x8 != 0 } func (i qcInfo) combinesBackward() bool { return i&0x4 != 0 } // == isMaybe func (i qcInfo) hasDecomposition() bool { return i&0x1 != 0 } // == isNoD +func (r runeInfo) isInert() bool { + return r.flags&0xf == 0 && r.ccc == 0 +} + // Wrappers for tables.go // The 16-bit value of the decompostion tries is an index into a byte // array of UTF-8 decomposition sequences. The first byte is the number // of bytes in the decomposition (excluding this length byte). The actual // sequence starts at the offset+1. -func decomposeNFC(b []byte) []byte { - p := nfcDecompTrie.lookupUnsafe(b) - n := decomps[p] - p++ - return decomps[p : p+uint16(n)] -} - -func decomposeNFKC(b []byte) []byte { - p := nfkcDecompTrie.lookupUnsafe(b) - n := decomps[p] - p++ - return decomps[p : p+uint16(n)] -} - -func decomposeStringNFC(s string) []byte { - p := nfcDecompTrie.lookupStringUnsafe(s) +func decomposeNFC(s input, i int) []byte { + p := s.decomposeNFC(i) n := decomps[p] p++ return decomps[p : p+uint16(n)] } -func decomposeStringNFKC(s string) []byte { - p := nfkcDecompTrie.lookupStringUnsafe(s) +func decomposeNFKC(s input, i int) []byte { + p := s.decomposeNFKC(i) n := decomps[p] p++ return decomps[p : p+uint16(n)] @@ -167,22 +146,12 @@ func combine(a, b uint32) uint32 { // 0..7 CCC value. // 8..11 qcInfo for NFC/NFD // 12..15 qcInfo for NFKC/NFKD -func lookupInfoNFC(b []byte) runeInfo { - v, sz := charInfoTrie.lookup(b) +func lookupInfoNFC(b input, i int) runeInfo { + v, sz := b.charinfo(i) return runeInfo{0, uint8(sz), uint8(v), qcInfo(v >> 8)} } -func lookupInfoStringNFC(s string) runeInfo { - v, sz := charInfoTrie.lookupString(s) - return runeInfo{0, uint8(sz), uint8(v), qcInfo(v >> 8)} -} - -func lookupInfoNFKC(b []byte) runeInfo { - v, sz := charInfoTrie.lookup(b) - return runeInfo{0, uint8(sz), uint8(v), qcInfo(v >> 12)} -} - -func lookupInfoStringNFKC(s string) runeInfo { - v, sz := charInfoTrie.lookupString(s) +func lookupInfoNFKC(b input, i int) runeInfo { + v, sz := b.charinfo(i) return runeInfo{0, uint8(sz), uint8(v), qcInfo(v >> 12)} } diff --git a/libgo/go/exp/norm/input.go b/libgo/go/exp/norm/input.go new file mode 100644 index 00000000000..12360a8fda1 --- /dev/null +++ b/libgo/go/exp/norm/input.go @@ -0,0 +1,107 @@ +// 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 "utf8" + +type input interface { + skipASCII(p int) int + skipNonStarter() int + appendSlice(buf []byte, s, e int) []byte + copySlice(buf []byte, s, e int) + charinfo(p int) (uint16, int) + decomposeNFC(p int) uint16 + decomposeNFKC(p int) uint16 + hangul(p int) uint32 +} + +type inputString string + +func (s inputString) skipASCII(p int) int { + for ; p < len(s) && s[p] < utf8.RuneSelf; p++ { + } + return p +} + +func (s inputString) skipNonStarter() int { + p := 0 + for ; p < len(s) && !utf8.RuneStart(s[p]); p++ { + } + return p +} + +func (s inputString) appendSlice(buf []byte, b, e int) []byte { + for i := b; i < e; i++ { + buf = append(buf, s[i]) + } + return buf +} + +func (s inputString) copySlice(buf []byte, b, e int) { + copy(buf, s[b:e]) +} + +func (s inputString) charinfo(p int) (uint16, int) { + return charInfoTrie.lookupString(string(s[p:])) +} + +func (s inputString) decomposeNFC(p int) uint16 { + return nfcDecompTrie.lookupStringUnsafe(string(s[p:])) +} + +func (s inputString) decomposeNFKC(p int) uint16 { + return nfkcDecompTrie.lookupStringUnsafe(string(s[p:])) +} + +func (s inputString) hangul(p int) uint32 { + if !isHangulString(string(s[p:])) { + return 0 + } + rune, _ := utf8.DecodeRuneInString(string(s[p:])) + return uint32(rune) +} + +type inputBytes []byte + +func (s inputBytes) skipASCII(p int) int { + for ; p < len(s) && s[p] < utf8.RuneSelf; p++ { + } + return p +} + +func (s inputBytes) skipNonStarter() int { + p := 0 + for ; p < len(s) && !utf8.RuneStart(s[p]); p++ { + } + return p +} + +func (s inputBytes) appendSlice(buf []byte, b, e int) []byte { + return append(buf, s[b:e]...) +} + +func (s inputBytes) copySlice(buf []byte, b, e int) { + copy(buf, s[b:e]) +} + +func (s inputBytes) charinfo(p int) (uint16, int) { + return charInfoTrie.lookup(s[p:]) +} + +func (s inputBytes) decomposeNFC(p int) uint16 { + return nfcDecompTrie.lookupUnsafe(s[p:]) +} + +func (s inputBytes) decomposeNFKC(p int) uint16 { + return nfkcDecompTrie.lookupUnsafe(s[p:]) +} + +func (s inputBytes) hangul(p int) uint32 { + if !isHangul(s[p:]) { + return 0 + } + rune, _ := utf8.DecodeRune(s[p:]) + return uint32(rune) +} diff --git a/libgo/go/exp/norm/maketables.go b/libgo/go/exp/norm/maketables.go index e3e5700a64e..14718c5cd22 100644 --- a/libgo/go/exp/norm/maketables.go +++ b/libgo/go/exp/norm/maketables.go @@ -515,9 +515,13 @@ func completeCharFields(form int) { f.quickCheck[MComposed] = QCNo case (i & 0xffff00) == JamoLBase: f.quickCheck[MComposed] = QCYes + if JamoLBase <= i && i < JamoLEnd { + f.combinesForward = true + } if JamoVBase <= i && i < JamoVEnd { f.quickCheck[MComposed] = QCMaybe f.combinesBackward = true + f.combinesForward = true } if JamoTBase <= i && i < JamoTEnd { f.quickCheck[MComposed] = QCMaybe @@ -562,7 +566,7 @@ func makeEntry(f *FormInfo) uint16 { case QCMaybe: e |= 0x6 default: - log.Fatalf("Illegal quickcheck value %d.", f.quickCheck[MComposed]) + log.Fatalf("Illegal quickcheck value %v.", f.quickCheck[MComposed]) } return e } diff --git a/libgo/go/exp/norm/maketesttables.go b/libgo/go/exp/norm/maketesttables.go index c5f6a64368d..fdcc114be23 100644 --- a/libgo/go/exp/norm/maketesttables.go +++ b/libgo/go/exp/norm/maketesttables.go @@ -21,6 +21,7 @@ var testRunes = []int{ 0x80, 0x100, 0x7FF, // 2-byte sequences 0x800, 0x999, 0xFFFF, // 3-byte sequences 0x10000, 0x10101, 0x10FFFF, // 4-byte sequences + 0x200, 0x201, 0x202, 0x210, 0x215, // five entries in one sparse block } const fileHeader = `// Generated by running diff --git a/libgo/go/exp/norm/normalize.go b/libgo/go/exp/norm/normalize.go index e9d18dd9ea9..391bc4184f2 100644 --- a/libgo/go/exp/norm/normalize.go +++ b/libgo/go/exp/norm/normalize.go @@ -5,6 +5,8 @@ // Package norm contains types and functions for normalizing Unicode strings. package norm +import "utf8" + // A Form denotes a canonical representation of Unicode code points. // The Unicode-defined normalization and equivalence forms are: // @@ -32,68 +34,431 @@ const ( // Bytes returns f(b). May return b if f(b) = b. func (f Form) Bytes(b []byte) []byte { - panic("not implemented") + n := f.QuickSpan(b) + if n == len(b) { + return b + } + out := make([]byte, n, len(b)) + copy(out, b[0:n]) + return f.Append(out, b[n:]...) } // String returns f(s). func (f Form) String(s string) string { - panic("not implemented") + n := f.QuickSpanString(s) + if n == len(s) { + return s + } + out := make([]byte, 0, len(s)) + copy(out, s[0:n]) + return string(f.AppendString(out, s[n:])) } // IsNormal returns true if b == f(b). func (f Form) IsNormal(b []byte) bool { - panic("not implemented") + rb := reorderBuffer{} + rb.init(f, b) + bp := quickSpan(&rb, 0) + if bp == len(b) { + return true + } + for bp < len(b) { + decomposeSegment(&rb, bp) + if rb.f.composing { + rb.compose() + } + for i := 0; i < rb.nrune; i++ { + info := rb.rune[i] + if bp+int(info.size) > len(b) { + return false + } + p := info.pos + pe := p + info.size + for ; p < pe; p++ { + if b[bp] != rb.byte[p] { + return false + } + bp++ + } + } + rb.reset() + bp = quickSpan(&rb, bp) + } + return true } // IsNormalString returns true if s == f(s). func (f Form) IsNormalString(s string) bool { - panic("not implemented") + rb := reorderBuffer{} + rb.initString(f, s) + bp := quickSpan(&rb, 0) + if bp == len(s) { + return true + } + for bp < len(s) { + decomposeSegment(&rb, bp) + if rb.f.composing { + rb.compose() + } + for i := 0; i < rb.nrune; i++ { + info := rb.rune[i] + if bp+int(info.size) > len(s) { + return false + } + p := info.pos + pe := p + info.size + for ; p < pe; p++ { + if s[bp] != rb.byte[p] { + return false + } + bp++ + } + } + rb.reset() + bp = quickSpan(&rb, bp) + } + return true +} + +// patchTail fixes a case where a rune may be incorrectly normalized +// if it is followed by illegal continuation bytes. It returns the +// patched buffer and the number of trailing continuation bytes that +// have been dropped. +func patchTail(rb *reorderBuffer, buf []byte) ([]byte, int) { + info, p := lastRuneStart(&rb.f, buf) + if p == -1 || info.size == 0 { + return buf, 0 + } + end := p + int(info.size) + extra := len(buf) - end + if extra > 0 { + buf = decomposeToLastBoundary(rb, buf[:end]) + if rb.f.composing { + rb.compose() + } + return rb.flush(buf), extra + } + return buf, 0 +} + +func appendQuick(rb *reorderBuffer, dst []byte, i int) ([]byte, int) { + if rb.nsrc == i { + return dst, i + } + end := quickSpan(rb, i) + return rb.src.appendSlice(dst, i, end), end } // Append returns f(append(out, b...)). -// The buffer out must be empty or equal to f(out). -func (f Form) Append(out, b []byte) []byte { - panic("not implemented") +// The buffer out must be nil, empty, or equal to f(out). +func (f Form) Append(out []byte, src ...byte) []byte { + if len(src) == 0 { + return out + } + rb := reorderBuffer{} + rb.init(f, src) + return doAppend(&rb, out) +} + +func doAppend(rb *reorderBuffer, out []byte) []byte { + src, n := rb.src, rb.nsrc + doMerge := len(out) > 0 + p := 0 + if p = src.skipNonStarter(); p > 0 { + // Move leading non-starters to destination. + out = src.appendSlice(out, 0, p) + buf, ndropped := patchTail(rb, out) + if ndropped > 0 { + out = src.appendSlice(buf, p-ndropped, p) + doMerge = false // no need to merge, ends with illegal UTF-8 + } else { + out = decomposeToLastBoundary(rb, buf) // force decomposition + } + } + fd := &rb.f + if doMerge { + var info runeInfo + if p < n { + info = fd.info(src, p) + if p == 0 && !fd.boundaryBefore(fd, info) { + out = decomposeToLastBoundary(rb, out) + } + } + if info.size == 0 || fd.boundaryBefore(fd, info) { + if fd.composing { + rb.compose() + } + out = rb.flush(out) + if info.size == 0 { + // Append incomplete UTF-8 encoding. + return src.appendSlice(out, p, n) + } + } + } + if rb.nrune == 0 { + out, p = appendQuick(rb, out, p) + } + for p < n { + p = decomposeSegment(rb, p) + if fd.composing { + rb.compose() + } + out = rb.flush(out) + out, p = appendQuick(rb, out, p) + } + return out } // AppendString returns f(append(out, []byte(s))). -// The buffer out must be empty or equal to f(out). -func (f Form) AppendString(out []byte, s string) []byte { - panic("not implemented") +// The buffer out must be nil, empty, or equal to f(out). +func (f Form) AppendString(out []byte, src string) []byte { + if len(src) == 0 { + return out + } + rb := reorderBuffer{} + rb.initString(f, src) + return doAppend(&rb, out) } // QuickSpan returns a boundary n such that b[0:n] == f(b[0:n]). // It is not guaranteed to return the largest such n. func (f Form) QuickSpan(b []byte) int { - panic("not implemented") + rb := reorderBuffer{} + rb.init(f, b) + return quickSpan(&rb, 0) +} + +func quickSpan(rb *reorderBuffer, i int) int { + var lastCC uint8 + var nc int + lastSegStart := i + src, n := rb.src, rb.nsrc + for i < n { + if j := src.skipASCII(i); i != j { + i = j + lastSegStart = i - 1 + lastCC = 0 + nc = 0 + continue + } + info := rb.f.info(src, i) + if info.size == 0 { + // include incomplete runes + return n + } + cc := info.ccc + if rb.f.composing { + if !info.flags.isYesC() { + break + } + } else { + if !info.flags.isYesD() { + break + } + } + if cc == 0 { + lastSegStart = i + nc = 0 + } else { + if nc >= maxCombiningChars { + lastSegStart = i + lastCC = cc + nc = 1 + } else { + if lastCC > cc { + return lastSegStart + } + nc++ + } + } + lastCC = cc + i += int(info.size) + } + if i == n { + return n + } + if rb.f.composing { + return lastSegStart + } + return i } // QuickSpanString returns a boundary n such that b[0:n] == f(s[0:n]). // It is not guaranteed to return the largest such n. func (f Form) QuickSpanString(s string) int { - panic("not implemented") + rb := reorderBuffer{} + rb.initString(f, s) + return quickSpan(&rb, 0) +} + +// FirstBoundary returns the position i of the first boundary in b +// or -1 if b contains no boundary. +func (f Form) FirstBoundary(b []byte) int { + rb := reorderBuffer{} + rb.init(f, b) + return firstBoundary(&rb) +} + +func firstBoundary(rb *reorderBuffer) int { + src, nsrc := rb.src, rb.nsrc + i := src.skipNonStarter() + if i >= nsrc { + return -1 + } + fd := &rb.f + info := fd.info(src, i) + for n := 0; info.size != 0 && !fd.boundaryBefore(fd, info); { + i += int(info.size) + if n++; n >= maxCombiningChars { + return i + } + if i >= nsrc { + if !fd.boundaryAfter(fd, info) { + return -1 + } + return nsrc + } + info = fd.info(src, i) + } + if info.size == 0 { + return -1 + } + return i +} + +// FirstBoundaryInString returns the position i of the first boundary in s +// or -1 if s contains no boundary. +func (f Form) FirstBoundaryInString(s string) int { + rb := reorderBuffer{} + rb.initString(f, s) + return firstBoundary(&rb) +} + +// LastBoundary returns the position i of the last boundary in b +// or -1 if b contains no boundary. +func (f Form) LastBoundary(b []byte) int { + return lastBoundary(formTable[f], b) } -// FirstBoundary returns the position i of the first boundary in b. -// It returns len(b), false if b contains no boundaries. -func (f Form) FirstBoundary(b []byte) (i int, ok bool) { - panic("not implemented") +func lastBoundary(fd *formInfo, b []byte) int { + i := len(b) + info, p := lastRuneStart(fd, b) + if p == -1 { + return -1 + } + if info.size == 0 { // ends with incomplete rune + if p == 0 { // starts wtih incomplete rune + return -1 + } + i = p + info, p = lastRuneStart(fd, b[:i]) + if p == -1 { // incomplete UTF-8 encoding or non-starter bytes without a starter + return i + } + } + if p+int(info.size) != i { // trailing non-starter bytes: illegal UTF-8 + return i + } + if fd.boundaryAfter(fd, info) { + return i + } + i = p + for n := 0; i >= 0 && !fd.boundaryBefore(fd, info); { + info, p = lastRuneStart(fd, b[:i]) + if n++; n >= maxCombiningChars { + return len(b) + } + if p+int(info.size) != i { + if p == -1 { // no boundary found + return -1 + } + return i // boundary after an illegal UTF-8 encoding + } + i = p + } + return i } -// FirstBoundaryInString return the position i of the first boundary in s. -// It returns len(s), false if s contains no boundaries. -func (f Form) FirstBoundaryInString(s string) (i int, ok bool) { - panic("not implemented") +// decomposeSegment scans the first segment in src into rb. +// It returns the number of bytes consumed from src. +// TODO(mpvl): consider inserting U+034f (Combining Grapheme Joiner) +// when we detect a sequence of 30+ non-starter chars. +func decomposeSegment(rb *reorderBuffer, sp int) int { + // Force one character to be consumed. + info := rb.f.info(rb.src, sp) + if info.size == 0 { + return 0 + } + for rb.insert(rb.src, sp, info) { + sp += int(info.size) + if sp >= rb.nsrc { + break + } + info = rb.f.info(rb.src, sp) + bound := rb.f.boundaryBefore(&rb.f, info) + if bound || info.size == 0 { + break + } + } + return sp } -// LastBoundaryIn returns the position i of the last boundary in b. -// It returns 0, false if b contains no boundary. -func (f Form) LastBoundary(b []byte) (i int, ok bool) { - panic("not implemented") +// lastRuneStart returns the runeInfo and position of the last +// rune in buf or the zero runeInfo and -1 if no rune was found. +func lastRuneStart(fd *formInfo, buf []byte) (runeInfo, int) { + p := len(buf) - 1 + for ; p >= 0 && !utf8.RuneStart(buf[p]); p-- { + } + if p < 0 { + return runeInfo{0, 0, 0, 0}, -1 + } + return fd.info(inputBytes(buf), p), p } -// LastBoundaryInString returns the position i of the last boundary in s. -// It returns 0, false if s contains no boundary. -func (f Form) LastBoundaryInString(s string) (i int, ok bool) { - panic("not implemented") +// decomposeToLastBoundary finds an open segment at the end of the buffer +// and scans it into rb. Returns the buffer minus the last segment. +func decomposeToLastBoundary(rb *reorderBuffer, buf []byte) []byte { + fd := &rb.f + info, i := lastRuneStart(fd, buf) + if int(info.size) != len(buf)-i { + // illegal trailing continuation bytes + return buf + } + if rb.f.boundaryAfter(fd, info) { + return buf + } + var add [maxBackRunes]runeInfo // stores runeInfo in reverse order + add[0] = info + padd := 1 + n := 1 + p := len(buf) - int(info.size) + for ; p >= 0 && !rb.f.boundaryBefore(fd, info); p -= int(info.size) { + info, i = lastRuneStart(fd, buf[:p]) + if int(info.size) != p-i { + break + } + // Check that decomposition doesn't result in overflow. + if info.flags.hasDecomposition() { + dcomp := rb.f.decompose(inputBytes(buf), p-int(info.size)) + for i := 0; i < len(dcomp); { + inf := rb.f.info(inputBytes(dcomp), i) + i += int(inf.size) + n++ + } + } else { + n++ + } + if n > maxBackRunes { + break + } + add[padd] = info + padd++ + } + pp := p + for padd--; padd >= 0; padd-- { + info = add[padd] + rb.insert(inputBytes(buf), pp, info) + pp += int(info.size) + } + return buf[:p] } diff --git a/libgo/go/exp/norm/normalize_test.go b/libgo/go/exp/norm/normalize_test.go new file mode 100644 index 00000000000..e374edf0abb --- /dev/null +++ b/libgo/go/exp/norm/normalize_test.go @@ -0,0 +1,644 @@ +// 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" +) + +type PositionTest struct { + input string + pos int + buffer string // expected contents of reorderBuffer, if applicable +} + +type positionFunc func(rb *reorderBuffer, s string) int + +func runPosTests(t *testing.T, name string, f Form, fn positionFunc, tests []PositionTest) { + rb := reorderBuffer{} + rb.init(f, nil) + for i, test := range tests { + rb.reset() + rb.src = inputString(test.input) + rb.nsrc = len(test.input) + pos := fn(&rb, test.input) + if pos != test.pos { + t.Errorf("%s:%d: position is %d; want %d", name, i, pos, test.pos) + } + runes := []int(test.buffer) + if rb.nrune != len(runes) { + t.Errorf("%s:%d: reorder buffer lenght is %d; want %d", name, i, rb.nrune, len(runes)) + continue + } + for j, want := range runes { + found := int(rb.runeAt(j)) + if found != want { + t.Errorf("%s:%d: rune at %d is %U; want %U", name, i, j, found, want) + } + } + } +} + +var decomposeSegmentTests = []PositionTest{ + // illegal runes + {"\xC0", 0, ""}, + {"\u00E0\x80", 2, "\u0061\u0300"}, + // starter + {"a", 1, "a"}, + {"ab", 1, "a"}, + // starter + composing + {"a\u0300", 3, "a\u0300"}, + {"a\u0300b", 3, "a\u0300"}, + // with decomposition + {"\u00C0", 2, "A\u0300"}, + {"\u00C0b", 2, "A\u0300"}, + // long + {strings.Repeat("\u0300", 31), 62, strings.Repeat("\u0300", 31)}, + // ends with incomplete UTF-8 encoding + {"\xCC", 0, ""}, + {"\u0300\xCC", 2, "\u0300"}, +} + +func decomposeSegmentF(rb *reorderBuffer, s string) int { + rb.src = inputString(s) + rb.nsrc = len(s) + return decomposeSegment(rb, 0) +} + +func TestDecomposeSegment(t *testing.T) { + runPosTests(t, "TestDecomposeSegment", NFC, decomposeSegmentF, decomposeSegmentTests) +} + +var firstBoundaryTests = []PositionTest{ + // no boundary + {"", -1, ""}, + {"\u0300", -1, ""}, + {"\x80\x80", -1, ""}, + // illegal runes + {"\xff", 0, ""}, + {"\u0300\xff", 2, ""}, + {"\u0300\xc0\x80\x80", 2, ""}, + // boundaries + {"a", 0, ""}, + {"\u0300a", 2, ""}, + // Hangul + {"\u1103\u1161", 0, ""}, + {"\u110B\u1173\u11B7", 0, ""}, + {"\u1161\u110B\u1173\u11B7", 3, ""}, + {"\u1173\u11B7\u1103\u1161", 6, ""}, + // too many combining characters. + {strings.Repeat("\u0300", maxCombiningChars-1), -1, ""}, + {strings.Repeat("\u0300", maxCombiningChars), 60, ""}, + {strings.Repeat("\u0300", maxCombiningChars+1), 60, ""}, +} + +func firstBoundaryF(rb *reorderBuffer, s string) int { + return rb.f.form.FirstBoundary([]byte(s)) +} + +func firstBoundaryStringF(rb *reorderBuffer, s string) int { + return rb.f.form.FirstBoundaryInString(s) +} + +func TestFirstBoundary(t *testing.T) { + runPosTests(t, "TestFirstBoundary", NFC, firstBoundaryF, firstBoundaryTests) + runPosTests(t, "TestFirstBoundaryInString", NFC, firstBoundaryStringF, firstBoundaryTests) +} + +var decomposeToLastTests = []PositionTest{ + // ends with inert character + {"Hello!", 6, ""}, + {"\u0632", 2, ""}, + {"a\u0301\u0635", 5, ""}, + // ends with non-inert starter + {"a", 0, "a"}, + {"a\u0301a", 3, "a"}, + {"a\u0301\u03B9", 3, "\u03B9"}, + {"a\u0327", 0, "a\u0327"}, + // illegal runes + {"\xFF", 1, ""}, + {"aa\xFF", 3, ""}, + {"\xC0\x80\x80", 3, ""}, + {"\xCC\x80\x80", 3, ""}, + // ends with incomplete UTF-8 encoding + {"a\xCC", 2, ""}, + // ends with combining characters + {"\u0300\u0301", 0, "\u0300\u0301"}, + {"a\u0300\u0301", 0, "a\u0300\u0301"}, + {"a\u0301\u0308", 0, "a\u0301\u0308"}, + {"a\u0308\u0301", 0, "a\u0308\u0301"}, + {"aaaa\u0300\u0301", 3, "a\u0300\u0301"}, + {"\u0300a\u0300\u0301", 2, "a\u0300\u0301"}, + {"\u00C0", 0, "A\u0300"}, + {"a\u00C0", 1, "A\u0300"}, + // decomposing + {"a\u0300\uFDC0", 3, "\u0645\u062C\u064A"}, + {"\uFDC0" + strings.Repeat("\u0300", 26), 0, "\u0645\u062C\u064A" + strings.Repeat("\u0300", 26)}, + // Hangul + {"a\u1103", 1, "\u1103"}, + {"a\u110B", 1, "\u110B"}, + {"a\u110B\u1173", 1, "\u110B\u1173"}, + // See comment in composition.go:compBoundaryAfter. + {"a\u110B\u1173\u11B7", 1, "\u110B\u1173\u11B7"}, + {"a\uC73C", 1, "\u110B\u1173"}, + {"다음", 3, "\u110B\u1173\u11B7"}, + {"다", 0, "\u1103\u1161"}, + {"\u1103\u1161\u110B\u1173\u11B7", 6, "\u110B\u1173\u11B7"}, + {"\u110B\u1173\u11B7\u1103\u1161", 9, "\u1103\u1161"}, + {"다음음", 6, "\u110B\u1173\u11B7"}, + {"음다다", 6, "\u1103\u1161"}, + // buffer overflow + {"a" + strings.Repeat("\u0300", 30), 3, strings.Repeat("\u0300", 29)}, + {"\uFDFA" + strings.Repeat("\u0300", 14), 3, strings.Repeat("\u0300", 14)}, + // weird UTF-8 + {"a\u0300\u11B7", 0, "a\u0300\u11B7"}, +} + +func decomposeToLast(rb *reorderBuffer, s string) int { + buf := decomposeToLastBoundary(rb, []byte(s)) + return len(buf) +} + +func TestDecomposeToLastBoundary(t *testing.T) { + runPosTests(t, "TestDecomposeToLastBoundary", NFKC, decomposeToLast, decomposeToLastTests) +} + +var lastBoundaryTests = []PositionTest{ + // ends with inert character + {"Hello!", 6, ""}, + {"\u0632", 2, ""}, + // ends with non-inert starter + {"a", 0, ""}, + // illegal runes + {"\xff", 1, ""}, + {"aa\xff", 3, ""}, + {"a\xff\u0300", 1, ""}, + {"\xc0\x80\x80", 3, ""}, + {"\xc0\x80\x80\u0300", 3, ""}, + // ends with incomplete UTF-8 encoding + {"\xCC", -1, ""}, + {"\xE0\x80", -1, ""}, + {"\xF0\x80\x80", -1, ""}, + {"a\xCC", 0, ""}, + {"\x80\xCC", 1, ""}, + {"\xCC\xCC", 1, ""}, + // ends with combining characters + {"a\u0300\u0301", 0, ""}, + {"aaaa\u0300\u0301", 3, ""}, + {"\u0300a\u0300\u0301", 2, ""}, + {"\u00C0", 0, ""}, + {"a\u00C0", 1, ""}, + // decomposition may recombine + {"\u0226", 0, ""}, + // no boundary + {"", -1, ""}, + {"\u0300\u0301", -1, ""}, + {"\u0300", -1, ""}, + {"\x80\x80", -1, ""}, + {"\x80\x80\u0301", -1, ""}, + // Hangul + {"다음", 3, ""}, + {"다", 0, ""}, + {"\u1103\u1161\u110B\u1173\u11B7", 6, ""}, + {"\u110B\u1173\u11B7\u1103\u1161", 9, ""}, + // too many combining characters. + {strings.Repeat("\u0300", maxCombiningChars-1), -1, ""}, + {strings.Repeat("\u0300", maxCombiningChars), 60, ""}, + {strings.Repeat("\u0300", maxCombiningChars+1), 62, ""}, +} + +func lastBoundaryF(rb *reorderBuffer, s string) int { + return rb.f.form.LastBoundary([]byte(s)) +} + +func TestLastBoundary(t *testing.T) { + runPosTests(t, "TestLastBoundary", NFC, lastBoundaryF, lastBoundaryTests) +} + +var quickSpanTests = []PositionTest{ + {"", 0, ""}, + // starters + {"a", 1, ""}, + {"abc", 3, ""}, + {"\u043Eb", 3, ""}, + // incomplete last rune. + {"\xCC", 1, ""}, + {"a\xCC", 2, ""}, + // incorrectly ordered combining characters + {"\u0300\u0316", 0, ""}, + {"\u0300\u0316cd", 0, ""}, + // have a maximum number of combining characters. + {strings.Repeat("\u035D", 30) + "\u035B", 62, ""}, + {"a" + strings.Repeat("\u035D", 30) + "\u035B", 63, ""}, + {"Ɵ" + strings.Repeat("\u035D", 30) + "\u035B", 64, ""}, + {"aa" + strings.Repeat("\u035D", 30) + "\u035B", 64, ""}, +} + +var quickSpanNFDTests = []PositionTest{ + // needs decomposing + {"\u00C0", 0, ""}, + {"abc\u00C0", 3, ""}, + // correctly ordered combining characters + {"\u0300", 2, ""}, + {"ab\u0300", 4, ""}, + {"ab\u0300cd", 6, ""}, + {"\u0300cd", 4, ""}, + {"\u0316\u0300", 4, ""}, + {"ab\u0316\u0300", 6, ""}, + {"ab\u0316\u0300cd", 8, ""}, + {"ab\u0316\u0300\u00C0", 6, ""}, + {"\u0316\u0300cd", 6, ""}, + {"\u043E\u0308b", 5, ""}, + // incorrectly ordered combining characters + {"ab\u0300\u0316", 1, ""}, // TODO(mpvl): we could skip 'b' as well. + {"ab\u0300\u0316cd", 1, ""}, + // Hangul + {"같은", 0, ""}, +} + +var quickSpanNFCTests = []PositionTest{ + // okay composed + {"\u00C0", 2, ""}, + {"abc\u00C0", 5, ""}, + // correctly ordered combining characters + {"ab\u0300", 1, ""}, + {"ab\u0300cd", 1, ""}, + {"ab\u0316\u0300", 1, ""}, + {"ab\u0316\u0300cd", 1, ""}, + {"\u00C0\u035D", 4, ""}, + // we do not special case leading combining characters + {"\u0300cd", 0, ""}, + {"\u0300", 0, ""}, + {"\u0316\u0300", 0, ""}, + {"\u0316\u0300cd", 0, ""}, + // incorrectly ordered combining characters + {"ab\u0300\u0316", 1, ""}, + {"ab\u0300\u0316cd", 1, ""}, + // Hangul + {"같은", 6, ""}, +} + +func doQuickSpan(rb *reorderBuffer, s string) int { + return rb.f.form.QuickSpan([]byte(s)) +} + +func doQuickSpanString(rb *reorderBuffer, s string) int { + return rb.f.form.QuickSpanString(s) +} + +func TestQuickSpan(t *testing.T) { + runPosTests(t, "TestQuickSpanNFD1", NFD, doQuickSpan, quickSpanTests) + runPosTests(t, "TestQuickSpanNFD2", NFD, doQuickSpan, quickSpanNFDTests) + runPosTests(t, "TestQuickSpanNFC1", NFC, doQuickSpan, quickSpanTests) + runPosTests(t, "TestQuickSpanNFC2", NFC, doQuickSpan, quickSpanNFCTests) + + runPosTests(t, "TestQuickSpanStringNFD1", NFD, doQuickSpanString, quickSpanTests) + runPosTests(t, "TestQuickSpanStringNFD2", NFD, doQuickSpanString, quickSpanNFDTests) + runPosTests(t, "TestQuickSpanStringNFC1", NFC, doQuickSpanString, quickSpanTests) + runPosTests(t, "TestQuickSpanStringNFC2", NFC, doQuickSpanString, quickSpanNFCTests) +} + +var isNormalTests = []PositionTest{ + {"", 1, ""}, + // illegal runes + {"\xff", 1, ""}, + // starters + {"a", 1, ""}, + {"abc", 1, ""}, + {"\u043Eb", 1, ""}, + // incorrectly ordered combining characters + {"\u0300\u0316", 0, ""}, + {"ab\u0300\u0316", 0, ""}, + {"ab\u0300\u0316cd", 0, ""}, + {"\u0300\u0316cd", 0, ""}, +} +var isNormalNFDTests = []PositionTest{ + // needs decomposing + {"\u00C0", 0, ""}, + {"abc\u00C0", 0, ""}, + // correctly ordered combining characters + {"\u0300", 1, ""}, + {"ab\u0300", 1, ""}, + {"ab\u0300cd", 1, ""}, + {"\u0300cd", 1, ""}, + {"\u0316\u0300", 1, ""}, + {"ab\u0316\u0300", 1, ""}, + {"ab\u0316\u0300cd", 1, ""}, + {"\u0316\u0300cd", 1, ""}, + {"\u043E\u0308b", 1, ""}, + // Hangul + {"같은", 0, ""}, +} +var isNormalNFCTests = []PositionTest{ + // okay composed + {"\u00C0", 1, ""}, + {"abc\u00C0", 1, ""}, + // need reordering + {"a\u0300", 0, ""}, + {"a\u0300cd", 0, ""}, + {"a\u0316\u0300", 0, ""}, + {"a\u0316\u0300cd", 0, ""}, + // correctly ordered combining characters + {"ab\u0300", 1, ""}, + {"ab\u0300cd", 1, ""}, + {"ab\u0316\u0300", 1, ""}, + {"ab\u0316\u0300cd", 1, ""}, + {"\u00C0\u035D", 1, ""}, + {"\u0300", 1, ""}, + {"\u0316\u0300cd", 1, ""}, + // Hangul + {"같은", 1, ""}, +} + +func isNormalF(rb *reorderBuffer, s string) int { + if rb.f.form.IsNormal([]byte(s)) { + return 1 + } + return 0 +} + +func TestIsNormal(t *testing.T) { + runPosTests(t, "TestIsNormalNFD1", NFD, isNormalF, isNormalTests) + runPosTests(t, "TestIsNormalNFD2", NFD, isNormalF, isNormalNFDTests) + runPosTests(t, "TestIsNormalNFC1", NFC, isNormalF, isNormalTests) + runPosTests(t, "TestIsNormalNFC2", NFC, isNormalF, isNormalNFCTests) +} + +type AppendTest struct { + left string + right string + out string +} + +type appendFunc func(f Form, out []byte, s string) []byte + +func runAppendTests(t *testing.T, name string, f Form, fn appendFunc, tests []AppendTest) { + for i, test := range tests { + out := []byte(test.left) + out = fn(f, out, test.right) + outs := string(out) + if len(outs) != len(test.out) { + t.Errorf("%s:%d: length is %d; want %d", name, i, len(outs), len(test.out)) + } + if outs != test.out { + // Find first rune that differs and show context. + ir := []int(outs) + ig := []int(test.out) + 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++ { + t.Errorf("%s:%d: runeAt(%d) = %U; want %U", name, i, j, ir[j], ig[j]) + } + break + } + } + } +} + +var appendTests = []AppendTest{ + // empty buffers + {"", "", ""}, + {"a", "", "a"}, + {"", "a", "a"}, + {"", "\u0041\u0307\u0304", "\u01E0"}, + // segment split across buffers + {"", "a\u0300b", "\u00E0b"}, + {"a", "\u0300b", "\u00E0b"}, + {"a", "\u0300\u0316", "\u00E0\u0316"}, + {"a", "\u0316\u0300", "\u00E0\u0316"}, + {"a", "\u0300a\u0300", "\u00E0\u00E0"}, + {"a", "\u0300a\u0300a\u0300", "\u00E0\u00E0\u00E0"}, + {"a", "\u0300aaa\u0300aaa\u0300", "\u00E0aa\u00E0aa\u00E0"}, + {"a\u0300", "\u0327", "\u00E0\u0327"}, + {"a\u0327", "\u0300", "\u00E0\u0327"}, + {"a\u0316", "\u0300", "\u00E0\u0316"}, + {"\u0041\u0307", "\u0304", "\u01E0"}, + // Hangul + {"", "\u110B\u1173", "\uC73C"}, + {"", "\u1103\u1161", "\uB2E4"}, + {"", "\u110B\u1173\u11B7", "\uC74C"}, + {"", "\u320E", "\x28\uAC00\x29"}, + {"", "\x28\u1100\u1161\x29", "\x28\uAC00\x29"}, + {"\u1103", "\u1161", "\uB2E4"}, + {"\u110B", "\u1173\u11B7", "\uC74C"}, + {"\u110B\u1173", "\u11B7", "\uC74C"}, + {"\uC73C", "\u11B7", "\uC74C"}, + // UTF-8 encoding split across buffers + {"a\xCC", "\x80", "\u00E0"}, + {"a\xCC", "\x80b", "\u00E0b"}, + {"a\xCC", "\x80a\u0300", "\u00E0\u00E0"}, + {"a\xCC", "\x80\x80", "\u00E0\x80"}, + {"a\xCC", "\x80\xCC", "\u00E0\xCC"}, + {"a\u0316\xCC", "\x80a\u0316\u0300", "\u00E0\u0316\u00E0\u0316"}, + // ending in incomplete UTF-8 encoding + {"", "\xCC", "\xCC"}, + {"a", "\xCC", "a\xCC"}, + {"a", "b\xCC", "ab\xCC"}, + {"\u0226", "\xCC", "\u0226\xCC"}, + // illegal runes + {"", "\x80", "\x80"}, + {"", "\x80\x80\x80", "\x80\x80\x80"}, + {"", "\xCC\x80\x80\x80", "\xCC\x80\x80\x80"}, + {"", "a\x80", "a\x80"}, + {"", "a\x80\x80\x80", "a\x80\x80\x80"}, + {"", "a\x80\x80\x80\x80\x80\x80", "a\x80\x80\x80\x80\x80\x80"}, + {"a", "\x80\x80\x80", "a\x80\x80\x80"}, + // overflow + {"", strings.Repeat("\x80", 33), strings.Repeat("\x80", 33)}, + {strings.Repeat("\x80", 33), "", strings.Repeat("\x80", 33)}, + {strings.Repeat("\x80", 33), strings.Repeat("\x80", 33), strings.Repeat("\x80", 66)}, + // overflow of combining characters + {strings.Repeat("\u0300", 33), "", strings.Repeat("\u0300", 33)}, + // weird UTF-8 + {"\u00E0\xE1", "\x86", "\u00E0\xE1\x86"}, + {"a\u0300\u11B7", "\u0300", "\u00E0\u11B7\u0300"}, + {"a\u0300\u11B7\u0300", "\u0300", "\u00E0\u11B7\u0300\u0300"}, + {"\u0300", "\xF8\x80\x80\x80\x80\u0300", "\u0300\xF8\x80\x80\x80\x80\u0300"}, + {"\u0300", "\xFC\x80\x80\x80\x80\x80\u0300", "\u0300\xFC\x80\x80\x80\x80\x80\u0300"}, + {"\xF8\x80\x80\x80\x80\u0300", "\u0300", "\xF8\x80\x80\x80\x80\u0300\u0300"}, + {"\xFC\x80\x80\x80\x80\x80\u0300", "\u0300", "\xFC\x80\x80\x80\x80\x80\u0300\u0300"}, +} + +func appendF(f Form, out []byte, s string) []byte { + return f.Append(out, []byte(s)...) +} + +func appendStringF(f Form, out []byte, s string) []byte { + return f.AppendString(out, s) +} + +func TestAppend(t *testing.T) { + runAppendTests(t, "TestAppend", NFKC, appendF, appendTests) + runAppendTests(t, "TestAppendString", NFKC, appendStringF, appendTests) +} + +func doFormBenchmark(b *testing.B, f Form, s string) { + b.StopTimer() + in := []byte(s) + buf := make([]byte, 2*len(in)) + b.SetBytes(int64(len(s))) + b.StartTimer() + for i := 0; i < b.N; i++ { + buf = f.Append(buf[0:0], in...) + buf = buf[0:0] + } +} + +var ascii = strings.Repeat("There is nothing to change here! ", 500) + +func BenchmarkNormalizeAsciiNFC(b *testing.B) { + doFormBenchmark(b, NFC, ascii) +} +func BenchmarkNormalizeAsciiNFD(b *testing.B) { + doFormBenchmark(b, NFD, ascii) +} +func BenchmarkNormalizeAsciiNFKC(b *testing.B) { + doFormBenchmark(b, NFKC, ascii) +} +func BenchmarkNormalizeAsciiNFKD(b *testing.B) { + doFormBenchmark(b, NFKD, ascii) +} + +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)) + 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...) + } +} + +func BenchmarkCanonicalOrdering(b *testing.B) { + doTextBenchmark(b, txt_canon) +} +func BenchmarkExtendedLatin(b *testing.B) { + doTextBenchmark(b, txt_vn) +} +func BenchmarkMiscTwoByteUtf8(b *testing.B) { + doTextBenchmark(b, twoByteUtf8) +} +func BenchmarkMiscThreeByteUtf8(b *testing.B) { + doTextBenchmark(b, threeByteUtf8) +} +func BenchmarkHangul(b *testing.B) { + doTextBenchmark(b, txt_kr) +} +func BenchmarkJapanese(b *testing.B) { + doTextBenchmark(b, txt_jp) +} +func BenchmarkChinese(b *testing.B) { + doTextBenchmark(b, txt_cn) +} + +// Tests sampled from the Canonical ordering tests (Part 2) of +// http://unicode.org/Public/UNIDATA/NormalizationTest.txt +const txt_canon = `\u0061\u0315\u0300\u05AE\u0300\u0062 \u0061\u0300\u0315\u0300\u05AE\u0062 +\u0061\u0302\u0315\u0300\u05AE\u0062 \u0061\u0307\u0315\u0300\u05AE\u0062 +\u0061\u0315\u0300\u05AE\u030A\u0062 \u0061\u059A\u0316\u302A\u031C\u0062 +\u0061\u032E\u059A\u0316\u302A\u0062 \u0061\u0338\u093C\u0334\u0062 +\u0061\u059A\u0316\u302A\u0339 \u0061\u0341\u0315\u0300\u05AE\u0062 +\u0061\u0348\u059A\u0316\u302A\u0062 \u0061\u0361\u0345\u035D\u035C\u0062 +\u0061\u0366\u0315\u0300\u05AE\u0062 \u0061\u0315\u0300\u05AE\u0486\u0062 +\u0061\u05A4\u059A\u0316\u302A\u0062 \u0061\u0315\u0300\u05AE\u0613\u0062 +\u0061\u0315\u0300\u05AE\u0615\u0062 \u0061\u0617\u0315\u0300\u05AE\u0062 +\u0061\u0619\u0618\u064D\u064E\u0062 \u0061\u0315\u0300\u05AE\u0654\u0062 +\u0061\u0315\u0300\u05AE\u06DC\u0062 \u0061\u0733\u0315\u0300\u05AE\u0062 +\u0061\u0744\u059A\u0316\u302A\u0062 \u0061\u0315\u0300\u05AE\u0745\u0062 +\u0061\u09CD\u05B0\u094D\u3099\u0062 \u0061\u0E38\u0E48\u0E38\u0C56\u0062 +\u0061\u0EB8\u0E48\u0E38\u0E49\u0062 \u0061\u0F72\u0F71\u0EC8\u0F71\u0062 +\u0061\u1039\u05B0\u094D\u3099\u0062 \u0061\u05B0\u094D\u3099\u1A60\u0062 +\u0061\u3099\u093C\u0334\u1BE6\u0062 \u0061\u3099\u093C\u0334\u1C37\u0062 +\u0061\u1CD9\u059A\u0316\u302A\u0062 \u0061\u2DED\u0315\u0300\u05AE\u0062 +\u0061\u2DEF\u0315\u0300\u05AE\u0062 \u0061\u302D\u302E\u059A\u0316\u0062` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/vn/ +const txt_vn = `Với các điều kiện sau: Ghi nhận công của tác giả. +Nếu bạn sử dụng, chuyển đổi, hoặc xây dựng dự án từ +nội dung được chia sẻ này, bạn phải áp dụng giấy phép này hoặc +một giấy phép khác có các điều khoản tương tự như giấy phép này +cho dự án của bạn. Hiểu rằng: Miễn — Bất kỳ các điều kiện nào +trên đây cũng có thể được miễn bỏ nếu bạn được sự cho phép của +người sở hữu bản quyền. Phạm vi công chúng — Khi tác phẩm hoặc +bất kỳ chương nào của tác phẩm đã trong vùng dành cho công +chúng theo quy định của pháp luật thì tình trạng của nó không +bị ảnh hưởng bởi giấy phép trong bất kỳ trường hợp nào.` + +// Taken from http://creativecommons.org/licenses/by-sa/1.0/deed.ru +const txt_ru = `При обязательном соблюдении следующих условий: +Attribution — Вы должны атрибутировать произведение (указывать +автора и источник) в порядке, предусмотренном автором или +лицензиаром (но только так, чтобы никоим образом не подразумевалось, +что они поддерживают вас или использование вами данного произведения). +Υπό τις ακόλουθες προϋποθέσεις:` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/gr/ +const txt_gr = `Αναφορά Δημιουργού — Θα πρέπει να κάνετε την αναφορά στο έργο με τον +τρόπο που έχει οριστεί από το δημιουργό ή το χορηγούντο την άδεια +(χωρίς όμως να εννοείται με οποιονδήποτε τρόπο ότι εγκρίνουν εσάς ή +τη χρήση του έργου από εσάς). Παρόμοια Διανομή — Εάν αλλοιώσετε, +τροποποιήσετε ή δημιουργήσετε περαιτέρω βασισμένοι στο έργο θα +μπορείτε να διανέμετε το έργο που θα προκύψει μόνο με την ίδια ή +παρόμοια άδεια.` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/deed.ar +const txt_ar = `بموجب الشروط التالية نسب المصنف — يجب عليك أن +تنسب العمل بالطريقة التي تحددها المؤلف أو المرخص (ولكن ليس بأي حال من +الأحوال أن توحي وتقترح بتحول أو استخدامك للعمل). +المشاركة على قدم المساواة — إذا كنت يعدل ، والتغيير ، أو الاستفادة +من هذا العمل ، قد ينتج عن توزيع العمل إلا في ظل تشابه او تطابق فى واحد +لهذا الترخيص.` + +// Taken from http://creativecommons.org/licenses/by-sa/1.0/il/ +const txt_il = `בכפוף לתנאים הבאים: ייחוס — עליך לייחס את היצירה (לתת קרדיט) באופן +המצויין על-ידי היוצר או מעניק הרישיון (אך לא בשום אופן המרמז על כך +שהם תומכים בך או בשימוש שלך ביצירה). שיתוף זהה — אם תחליט/י לשנות, +לעבד או ליצור יצירה נגזרת בהסתמך על יצירה זו, תוכל/י להפיץ את יצירתך +החדשה רק תחת אותו הרישיון או רישיון דומה לרישיון זה.` + +const twoByteUtf8 = txt_ru + txt_gr + txt_ar + txt_il + +// Taken from http://creativecommons.org/licenses/by-sa/2.0/kr/ +const txt_kr = `다음과 같은 조건을 따라야 합니다: 저작자표시 +(Attribution) — 저작자나 이용허락자가 정한 방법으로 저작물의 +원저작자를 표시하여야 합니다(그러나 원저작자가 이용자나 이용자의 +이용을 보증하거나 추천한다는 의미로 표시해서는 안됩니다). +동일조건변경허락 — 이 저작물을 이용하여 만든 이차적 저작물에는 본 +라이선스와 동일한 라이선스를 적용해야 합니다.` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/th/ +const txt_th = `ภายใต้เงื่อนไข ดังต่อไปนี้ : แสดงที่มา — คุณต้องแสดงที่ +มาของงานดังกล่าว ตามรูปแบบที่ผู้สร้างสรรค์หรือผู้อนุญาตกำหนด (แต่ +ไม่ใช่ในลักษณะที่ว่า พวกเขาสนับสนุนคุณหรือสนับสนุนการที่ +คุณนำงานไปใช้) อนุญาตแบบเดียวกัน — หากคุณดัดแปลง เปลี่ยนรูป หรื +อต่อเติมงานนี้ คุณต้องใช้สัญญาอนุญาตแบบเดียวกันหรือแบบที่เหมื +อนกับสัญญาอนุญาตที่ใช้กับงานนี้เท่านั้น` + +const threeByteUtf8 = txt_th + +// Taken from http://creativecommons.org/licenses/by-sa/2.0/jp/ +const txt_jp = `あなたの従うべき条件は以下の通りです。 +表示 — あなたは原著作者のクレジットを表示しなければなりません。 +継承 — もしあなたがこの作品を改変、変形または加工した場合、 +あなたはその結果生じた作品をこの作品と同一の許諾条件の下でのみ +頒布することができます。` + +// http://creativecommons.org/licenses/by-sa/2.5/cn/ +const txt_cn = `您可以自由: 复制、发行、展览、表演、放映、 +广播或通过信息网络传播本作品 创作演绎作品 +对本作品进行商业性使用 惟须遵守下列条件: +署名 — 您必须按照作者或者许可人指定的方式对作品进行署名。 +相同方式共享 — 如果您改变、转换本作品或者以本作品为基础进行创作, +您只能采用与本协议相同的许可协议发布基于本作品的演绎作品。` diff --git a/libgo/go/exp/norm/normregtest.go b/libgo/go/exp/norm/normregtest.go new file mode 100644 index 00000000000..cbd73ffa759 --- /dev/null +++ b/libgo/go/exp/norm/normregtest.go @@ -0,0 +1,295 @@ +// 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 main + +import ( + "bufio" + "bytes" + "exp/norm" + "flag" + "fmt" + "http" + "log" + "os" + "path" + "regexp" + "runtime" + "strings" + "strconv" + "time" + "utf8" +) + +func main() { + flag.Parse() + loadTestData() + CharacterByCharacterTests() + StandardTests() + PerformanceTest() + if errorCount == 0 { + fmt.Println("PASS") + } +} + +const file = "NormalizationTest.txt" + +var url = flag.String("url", + "http://www.unicode.org/Public/6.0.0/ucd/"+file, + "URL of Unicode database directory") +var localFiles = flag.Bool("local", + false, + "data files have been copied to the current directory; for debugging only") + +var logger = log.New(os.Stderr, "", log.Lshortfile) + +// This regression test runs the test set in NormalizationTest.txt +// (taken from http://www.unicode.org/Public/6.0.0/ucd/). +// +// NormalizationTest.txt has form: +// @Part0 # Specific cases +// # +// 1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE +// 1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW +// +// Each test has 5 columns (c1, c2, c3, c4, c5), where +// (c1, c2, c3, c4, c5) == (c1, NFC(c1), NFD(c1), NFKC(c1), NFKD(c1)) +// +// CONFORMANCE: +// 1. The following invariants must be true for all conformant implementations +// +// NFC +// c2 == NFC(c1) == NFC(c2) == NFC(c3) +// c4 == NFC(c4) == NFC(c5) +// +// NFD +// c3 == NFD(c1) == NFD(c2) == NFD(c3) +// c5 == NFD(c4) == NFD(c5) +// +// NFKC +// c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5) +// +// NFKD +// c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5) +// +// 2. For every code point X assigned in this version of Unicode that is not +// specifically listed in Part 1, the following invariants must be true +// for all conformant implementations: +// +// X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X) +// + +// Column types. +const ( + cRaw = iota + cNFC + cNFD + cNFKC + cNFKD + cMaxColumns +) + +// Holds data from NormalizationTest.txt +var part []Part + +type Part struct { + name string + number int + tests []Test +} + +type Test struct { + name string + partnr int + number int + rune int // used for character by character test + cols [cMaxColumns]string // Each has 5 entries, see below. +} + +func (t Test) Name() string { + if t.number < 0 { + return part[t.partnr].name + } + return fmt.Sprintf("%s:%d", part[t.partnr].name, t.number) +} + +var partRe = regexp.MustCompile(`@Part(\d) # (.*)\n$`) +var testRe = regexp.MustCompile(`^` + strings.Repeat(`([\dA-F ]+);`, 5) + ` # (.*)\n?$`) + +var counter int + +// Load the data form NormalizationTest.txt +func loadTestData() { + if *localFiles { + pwd, _ := os.Getwd() + *url = "file://" + path.Join(pwd, file) + } + t := &http.Transport{} + t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) + c := &http.Client{Transport: t} + resp, err := c.Get(*url) + if err != nil { + logger.Fatal(err) + } + if resp.StatusCode != 200 { + logger.Fatal("bad GET status for "+file, resp.Status) + } + f := resp.Body + defer f.Close() + input := bufio.NewReader(f) + for { + line, err := input.ReadString('\n') + if err != nil { + if err == os.EOF { + break + } + logger.Fatal(err) + } + if len(line) == 0 || line[0] == '#' { + continue + } + m := partRe.FindStringSubmatch(line) + if m != nil { + if len(m) < 3 { + logger.Fatal("Failed to parse Part: ", line) + } + i, err := strconv.Atoi(m[1]) + if err != nil { + logger.Fatal(err) + } + name := m[2] + part = append(part, Part{name: name[:len(name)-1], number: i}) + continue + } + m = testRe.FindStringSubmatch(line) + if m == nil || len(m) < 7 { + logger.Fatalf(`Failed to parse: "%s" result: %#v`, line, m) + } + test := Test{name: m[6], partnr: len(part) - 1, number: counter} + counter++ + for j := 1; j < len(m)-1; j++ { + for _, split := range strings.Split(m[j], " ") { + r, err := strconv.Btoui64(split, 16) + if err != nil { + logger.Fatal(err) + } + if test.rune == 0 { + // save for CharacterByCharacterTests + test.rune = int(r) + } + var buf [utf8.UTFMax]byte + sz := utf8.EncodeRune(buf[:], int(r)) + test.cols[j-1] += string(buf[:sz]) + } + } + part := &part[len(part)-1] + part.tests = append(part.tests, test) + } +} + +var fstr = []string{"NFC", "NFD", "NFKC", "NFKD"} + +var errorCount int + +func cmpResult(t *Test, name string, f norm.Form, gold, test, result string) { + if gold != result { + errorCount++ + if errorCount > 20 { + return + } + st, sr, sg := []int(test), []int(result), []int(gold) + logger.Printf("%s:%s: %s(%X)=%X; want:%X: %s", + t.Name(), name, fstr[f], st, sr, sg, t.name) + } +} + +func cmpIsNormal(t *Test, name string, f norm.Form, test string, result, want bool) { + if result != want { + errorCount++ + if errorCount > 20 { + return + } + logger.Printf("%s:%s: %s(%X)=%v; want: %v", t.Name(), name, fstr[f], []int(test), result, want) + } +} + +func doTest(t *Test, f norm.Form, gold, test string) { + result := f.Bytes([]byte(test)) + cmpResult(t, "Bytes", f, gold, test, string(result)) + 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)) + } + cmpIsNormal(t, "IsNormal", f, test, f.IsNormal([]byte(test)), test == gold) +} + +func doConformanceTests(t *Test, partn int) { + for i := 0; i <= 2; i++ { + doTest(t, norm.NFC, t.cols[1], t.cols[i]) + doTest(t, norm.NFD, t.cols[2], t.cols[i]) + doTest(t, norm.NFKC, t.cols[3], t.cols[i]) + doTest(t, norm.NFKD, t.cols[4], t.cols[i]) + } + for i := 3; i <= 4; i++ { + doTest(t, norm.NFC, t.cols[3], t.cols[i]) + doTest(t, norm.NFD, t.cols[4], t.cols[i]) + doTest(t, norm.NFKC, t.cols[3], t.cols[i]) + doTest(t, norm.NFKD, t.cols[4], t.cols[i]) + } +} + +func CharacterByCharacterTests() { + tests := part[1].tests + last := 0 + for i := 0; i <= len(tests); i++ { // last one is special case + var rune int + if i == len(tests) { + rune = 0x2FA1E // Don't have to go to 0x10FFFF + } else { + rune = tests[i].rune + } + for last++; last < rune; last++ { + // Check all characters that were not explicitly listed in the test. + t := &Test{partnr: 1, number: -1} + char := string(last) + doTest(t, norm.NFC, char, char) + doTest(t, norm.NFD, char, char) + doTest(t, norm.NFKC, char, char) + doTest(t, norm.NFKD, char, char) + } + if i < len(tests) { + doConformanceTests(&tests[i], 1) + } + } +} + +func StandardTests() { + for _, j := range []int{0, 2, 3} { + for _, test := range part[j].tests { + doConformanceTests(&test, j) + } + } +} + +// PerformanceTest verifies that normalization is O(n). If any of the +// code does not properly check for maxCombiningChars, normalization +// may exhibit O(n**2) behavior. +func PerformanceTest() { + runtime.GOMAXPROCS(2) + success := make(chan bool, 1) + go func() { + buf := bytes.Repeat([]byte("\u035D"), 1024*1024) + buf = append(buf, "\u035B"...) + norm.NFC.Append(nil, buf...) + success <- true + }() + timeout := time.After(1e9) + select { + case <-success: + // test completed before the timeout + case <-timeout: + errorCount++ + logger.Printf(`unexpectedly long time to complete PerformanceTest`) + } +} diff --git a/libgo/go/exp/norm/readwriter.go b/libgo/go/exp/norm/readwriter.go new file mode 100644 index 00000000000..48ae1356201 --- /dev/null +++ b/libgo/go/exp/norm/readwriter.go @@ -0,0 +1,129 @@ +// 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 ( + "io" + "os" +) + +type normWriter struct { + rb reorderBuffer + w io.Writer + buf []byte +} + +// Write implements the standard write interface. If the last characters are +// not at a normalization boundary, the bytes will be buffered for the next +// write. The remaining bytes will be written on close. +func (w *normWriter) Write(data []byte) (n int, err os.Error) { + // Process data in pieces to keep w.buf size bounded. + const chunk = 4000 + + for len(data) > 0 { + // Normalize into w.buf. + m := len(data) + if m > chunk { + m = chunk + } + w.rb.src = inputBytes(data[:m]) + w.rb.nsrc = m + w.buf = doAppend(&w.rb, w.buf) + data = data[m:] + n += m + + // Write out complete prefix, save remainder. + // Note that lastBoundary looks back at most 30 runes. + i := lastBoundary(&w.rb.f, w.buf) + if i == -1 { + i = 0 + } + if i > 0 { + if _, err = w.w.Write(w.buf[:i]); err != nil { + break + } + bn := copy(w.buf, w.buf[i:]) + w.buf = w.buf[:bn] + } + } + return n, err +} + +// Close forces data that remains in the buffer to be written. +func (w *normWriter) Close() os.Error { + if len(w.buf) > 0 { + _, err := w.w.Write(w.buf) + if err != nil { + return err + } + } + return nil +} + +// Writer returns a new writer that implements Write(b) +// by writing f(b) to w. The returned writer may use an +// an internal buffer to maintain state across Write calls. +// Calling its Close method writes any buffered data to w. +func (f Form) Writer(w io.Writer) io.WriteCloser { + wr := &normWriter{rb: reorderBuffer{}, w: w} + wr.rb.init(f, nil) + return wr +} + +type normReader struct { + rb reorderBuffer + r io.Reader + inbuf []byte + outbuf []byte + bufStart int + lastBoundary int + err os.Error +} + +// Read implements the standard read interface. +func (r *normReader) Read(p []byte) (int, os.Error) { + for { + if r.lastBoundary-r.bufStart > 0 { + n := copy(p, r.outbuf[r.bufStart:r.lastBoundary]) + r.bufStart += n + if r.lastBoundary-r.bufStart > 0 { + return n, nil + } + return n, r.err + } + if r.err != nil { + return 0, r.err + } + outn := copy(r.outbuf, r.outbuf[r.lastBoundary:]) + r.outbuf = r.outbuf[0:outn] + r.bufStart = 0 + + n, err := r.r.Read(r.inbuf) + r.rb.src = inputBytes(r.inbuf[0:n]) + r.rb.nsrc, r.err = n, err + if n > 0 { + r.outbuf = doAppend(&r.rb, r.outbuf) + } + if err == os.EOF { + r.lastBoundary = len(r.outbuf) + } else { + r.lastBoundary = lastBoundary(&r.rb.f, r.outbuf) + if r.lastBoundary == -1 { + r.lastBoundary = 0 + } + } + } + panic("should not reach here") +} + +// Reader returns a new reader that implements Read +// by reading data from r and returning f(data). +func (f Form) Reader(r io.Reader) io.Reader { + const chunk = 4000 + buf := make([]byte, chunk) + rr := &normReader{rb: reorderBuffer{}, r: r, inbuf: buf} + rr.rb.init(f, buf) + return rr +} diff --git a/libgo/go/exp/norm/readwriter_test.go b/libgo/go/exp/norm/readwriter_test.go new file mode 100644 index 00000000000..68652efa65b --- /dev/null +++ b/libgo/go/exp/norm/readwriter_test.go @@ -0,0 +1,69 @@ +// 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 ( + "bytes" + "fmt" + "os" + "strings" + "testing" +) + +var ioTests = []AppendTest{ + {"", strings.Repeat("a\u0316\u0300", 6), strings.Repeat("\u00E0\u0316", 6)}, + {"", strings.Repeat("a\u0300\u0316", 4000), strings.Repeat("\u00E0\u0316", 4000)}, + {"", strings.Repeat("\x80\x80", 4000), strings.Repeat("\x80\x80", 4000)}, + {"", "\u0041\u0307\u0304", "\u01E0"}, +} + +var bufSizes = []int{1, 2, 3, 4, 5, 6, 7, 8, 100, 101, 102, 103, 4000, 4001, 4002, 4003} + +func readFunc(size int) appendFunc { + return func(f Form, out []byte, s string) []byte { + out = append(out, s...) + r := f.Reader(bytes.NewBuffer(out)) + buf := make([]byte, size) + result := []byte{} + for n, err := 0, os.Error(nil); err == nil; { + n, err = r.Read(buf) + result = append(result, buf[:n]...) + } + return result + } +} + +func TestReader(t *testing.T) { + for _, s := range bufSizes { + name := fmt.Sprintf("TestReader%da", s) + runAppendTests(t, name, NFKC, readFunc(s), appendTests) + name = fmt.Sprintf("TestReader%db", s) + runAppendTests(t, name, NFKC, readFunc(s), ioTests) + } +} + +func writeFunc(size int) appendFunc { + return func(f Form, out []byte, s string) []byte { + in := append(out, s...) + result := new(bytes.Buffer) + w := f.Writer(result) + buf := make([]byte, size) + for n := 0; len(in) > 0; in = in[n:] { + n = copy(buf, in) + _, _ = w.Write(buf[:n]) + } + w.Close() + return result.Bytes() + } +} + +func TestWriter(t *testing.T) { + for _, s := range bufSizes { + name := fmt.Sprintf("TestWriter%da", s) + runAppendTests(t, name, NFKC, writeFunc(s), appendTests) + name = fmt.Sprintf("TestWriter%db", s) + runAppendTests(t, name, NFKC, writeFunc(s), ioTests) + } +} diff --git a/libgo/go/exp/norm/tables.go b/libgo/go/exp/norm/tables.go index 76995c2fa18..55ff052dcbc 100644 --- a/libgo/go/exp/norm/tables.go +++ b/libgo/go/exp/norm/tables.go @@ -2490,545 +2490,632 @@ var decomps = [...]byte{ 0x98, 0x80, } -// nfcDecompValues: 4992 entries, 9984 bytes +// nfcDecompValues: 1408 entries, 2816 bytes // Block 2 is the null block. -var nfcDecompValues = [4992]uint16{ +var nfcDecompValues = [1408]uint16{ // Block 0x0, offset 0x0 // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x00c0: 0x0032, 0x00c1: 0x0036, 0x00c2: 0x003a, 0x00c3: 0x003e, 0x00c4: 0x0042, 0x00c5: 0x0046, - 0x00c7: 0x004a, 0x00c8: 0x004e, 0x00c9: 0x0052, 0x00ca: 0x0056, 0x00cb: 0x005a, - 0x00cc: 0x005e, 0x00cd: 0x0062, 0x00ce: 0x0066, 0x00cf: 0x006a, 0x00d1: 0x006e, - 0x00d2: 0x0072, 0x00d3: 0x0076, 0x00d4: 0x007a, 0x00d5: 0x007e, 0x00d6: 0x0082, - 0x00d9: 0x0086, 0x00da: 0x008a, 0x00db: 0x008e, 0x00dc: 0x0092, 0x00dd: 0x0096, - 0x00e0: 0x009a, 0x00e1: 0x009e, 0x00e2: 0x00a2, 0x00e3: 0x00a6, - 0x00e4: 0x00aa, 0x00e5: 0x00ae, 0x00e7: 0x00b2, 0x00e8: 0x00b6, 0x00e9: 0x00ba, - 0x00ea: 0x00be, 0x00eb: 0x00c2, 0x00ec: 0x00c6, 0x00ed: 0x00ca, 0x00ee: 0x00ce, 0x00ef: 0x00d2, - 0x00f1: 0x00d6, 0x00f2: 0x00da, 0x00f3: 0x00de, 0x00f4: 0x00e2, 0x00f5: 0x00e6, - 0x00f6: 0x00ea, 0x00f9: 0x00ee, 0x00fa: 0x00f2, 0x00fb: 0x00f6, - 0x00fc: 0x00fa, 0x00fd: 0x00fe, 0x00ff: 0x0102, + 0x00cd: 0x02fb, 0x00ce: 0x02ff, 0x00cf: 0x0303, 0x00d0: 0x0307, 0x00d1: 0x030b, + 0x00d2: 0x030f, 0x00d3: 0x0313, 0x00d4: 0x0317, 0x00d5: 0x031b, 0x00d6: 0x0321, 0x00d7: 0x0327, + 0x00d8: 0x032d, 0x00d9: 0x0333, 0x00da: 0x0339, 0x00db: 0x033f, 0x00dc: 0x0345, + 0x00de: 0x034b, 0x00df: 0x0351, 0x00e0: 0x0357, 0x00e1: 0x035d, 0x00e2: 0x0363, 0x00e3: 0x0368, + 0x00e6: 0x036d, 0x00e7: 0x0371, 0x00e8: 0x0375, 0x00e9: 0x0379, + 0x00ea: 0x037d, 0x00eb: 0x0381, 0x00ec: 0x0385, 0x00ed: 0x038b, 0x00ee: 0x0391, 0x00ef: 0x0396, + 0x00f0: 0x039b, 0x00f4: 0x03a8, 0x00f5: 0x03ac, + 0x00f8: 0x03b0, 0x00f9: 0x03b4, 0x00fa: 0x03b8, 0x00fb: 0x03be, + 0x00fc: 0x03c4, 0x00fd: 0x03c9, 0x00fe: 0x03ce, 0x00ff: 0x03d3, // Block 0x4, offset 0x100 - 0x0100: 0x0106, 0x0101: 0x010a, 0x0102: 0x010e, 0x0103: 0x0112, 0x0104: 0x0116, 0x0105: 0x011a, - 0x0106: 0x011e, 0x0107: 0x0122, 0x0108: 0x0126, 0x0109: 0x012a, 0x010a: 0x012e, 0x010b: 0x0132, - 0x010c: 0x0136, 0x010d: 0x013a, 0x010e: 0x013e, 0x010f: 0x0142, - 0x0112: 0x0146, 0x0113: 0x014a, 0x0114: 0x014e, 0x0115: 0x0152, 0x0116: 0x0156, 0x0117: 0x015a, - 0x0118: 0x015e, 0x0119: 0x0162, 0x011a: 0x0166, 0x011b: 0x016a, 0x011c: 0x016e, 0x011d: 0x0172, - 0x011e: 0x0176, 0x011f: 0x017a, 0x0120: 0x017e, 0x0121: 0x0182, 0x0122: 0x0186, 0x0123: 0x018a, - 0x0124: 0x018e, 0x0125: 0x0192, 0x0128: 0x0196, 0x0129: 0x019a, - 0x012a: 0x019e, 0x012b: 0x01a2, 0x012c: 0x01a6, 0x012d: 0x01aa, 0x012e: 0x01ae, 0x012f: 0x01b2, - 0x0130: 0x01b6, 0x0134: 0x01c0, 0x0135: 0x01c4, - 0x0136: 0x01c8, 0x0137: 0x01cc, 0x0139: 0x01d0, 0x013a: 0x01d4, 0x013b: 0x01d8, - 0x013c: 0x01dc, 0x013d: 0x01e0, 0x013e: 0x01e4, + 0x0100: 0x0b02, 0x0101: 0x0b06, 0x0102: 0x0b0a, 0x0103: 0x0b0e, 0x0104: 0x0b12, 0x0105: 0x0b16, + 0x0106: 0x0b1a, 0x0107: 0x0b1e, 0x0108: 0x0b22, 0x0109: 0x0b26, 0x010a: 0x0b2a, 0x010b: 0x0b2e, + 0x010c: 0x0b32, 0x010d: 0x0b38, 0x010e: 0x0b3e, 0x010f: 0x0b44, 0x0110: 0x0b4a, 0x0111: 0x0b50, + 0x0112: 0x0b56, 0x0113: 0x0b5c, 0x0114: 0x0b62, 0x0115: 0x0b66, 0x0116: 0x0b6a, 0x0117: 0x0b6e, + 0x0118: 0x0b72, 0x0119: 0x0b76, 0x011a: 0x0b7a, 0x011b: 0x0b7e, 0x011c: 0x0b82, 0x011d: 0x0b88, + 0x011e: 0x0b8e, 0x011f: 0x0b92, 0x0120: 0x0b96, 0x0121: 0x0b9a, 0x0122: 0x0b9e, 0x0123: 0x0ba2, + 0x0124: 0x0ba6, 0x0125: 0x0bac, 0x0126: 0x0bb2, 0x0127: 0x0bb8, 0x0128: 0x0bbe, 0x0129: 0x0bc4, + 0x012a: 0x0bca, 0x012b: 0x0bce, 0x012c: 0x0bd2, 0x012d: 0x0bd6, 0x012e: 0x0bda, 0x012f: 0x0bde, + 0x0130: 0x0be2, 0x0131: 0x0be6, 0x0132: 0x0bea, 0x0133: 0x0bee, 0x0134: 0x0bf2, 0x0135: 0x0bf6, + 0x0136: 0x0bfa, 0x0137: 0x0bfe, 0x0138: 0x0c02, 0x0139: 0x0c08, 0x013a: 0x0c0e, 0x013b: 0x0c14, + 0x013c: 0x0c1a, 0x013d: 0x0c1e, 0x013e: 0x0c22, 0x013f: 0x0c26, // Block 0x5, offset 0x140 - 0x0143: 0x01f0, 0x0144: 0x01f4, 0x0145: 0x01f8, - 0x0146: 0x01fc, 0x0147: 0x0200, 0x0148: 0x0204, - 0x014c: 0x020c, 0x014d: 0x0210, 0x014e: 0x0214, 0x014f: 0x0218, 0x0150: 0x021c, 0x0151: 0x0220, - 0x0154: 0x0224, 0x0155: 0x0228, 0x0156: 0x022c, 0x0157: 0x0230, - 0x0158: 0x0234, 0x0159: 0x0238, 0x015a: 0x023c, 0x015b: 0x0240, 0x015c: 0x0244, 0x015d: 0x0248, - 0x015e: 0x024c, 0x015f: 0x0250, 0x0160: 0x0254, 0x0161: 0x0258, 0x0162: 0x025c, 0x0163: 0x0260, - 0x0164: 0x0264, 0x0165: 0x0268, 0x0168: 0x026c, 0x0169: 0x0270, - 0x016a: 0x0274, 0x016b: 0x0278, 0x016c: 0x027c, 0x016d: 0x0280, 0x016e: 0x0284, 0x016f: 0x0288, - 0x0170: 0x028c, 0x0171: 0x0290, 0x0172: 0x0294, 0x0173: 0x0298, 0x0174: 0x029c, 0x0175: 0x02a0, - 0x0176: 0x02a4, 0x0177: 0x02a8, 0x0178: 0x02ac, 0x0179: 0x02b0, 0x017a: 0x02b4, 0x017b: 0x02b8, - 0x017c: 0x02bc, 0x017d: 0x02c0, 0x017e: 0x02c4, + 0x0140: 0x0c2a, 0x0141: 0x0c2e, 0x0142: 0x0c32, 0x0143: 0x0c36, 0x0144: 0x0c3a, 0x0145: 0x0c3e, + 0x0146: 0x0c42, 0x0147: 0x0c46, 0x0148: 0x0c4a, 0x0149: 0x0c4e, 0x014a: 0x0c52, 0x014b: 0x0c56, + 0x014c: 0x0c5a, 0x014d: 0x0c5e, 0x014e: 0x0c62, 0x014f: 0x0c66, 0x0150: 0x0c6a, 0x0151: 0x0c6e, + 0x0152: 0x0c72, 0x0153: 0x0c76, 0x0154: 0x0c7a, 0x0155: 0x0c7e, 0x0156: 0x0c82, 0x0157: 0x0c86, + 0x0158: 0x0c8a, 0x0159: 0x0c8e, 0x015b: 0x0c96, + 0x0160: 0x0c9b, 0x0161: 0x0c9f, 0x0162: 0x0ca3, 0x0163: 0x0ca7, + 0x0164: 0x0cab, 0x0165: 0x0cb1, 0x0166: 0x0cb7, 0x0167: 0x0cbd, 0x0168: 0x0cc3, 0x0169: 0x0cc9, + 0x016a: 0x0ccf, 0x016b: 0x0cd5, 0x016c: 0x0cdb, 0x016d: 0x0ce1, 0x016e: 0x0ce7, 0x016f: 0x0ced, + 0x0170: 0x0cf3, 0x0171: 0x0cf9, 0x0172: 0x0cff, 0x0173: 0x0d05, 0x0174: 0x0d0b, 0x0175: 0x0d11, + 0x0176: 0x0d17, 0x0177: 0x0d1d, 0x0178: 0x0d23, 0x0179: 0x0d27, 0x017a: 0x0d2b, 0x017b: 0x0d2f, + 0x017c: 0x0d33, 0x017d: 0x0d37, 0x017e: 0x0d3b, 0x017f: 0x0d41, // Block 0x6, offset 0x180 - 0x01a0: 0x02ca, 0x01a1: 0x02ce, - 0x01af: 0x02d2, - 0x01b0: 0x02d6, + 0x0180: 0x0d47, 0x0181: 0x0d4d, 0x0182: 0x0d53, 0x0183: 0x0d59, 0x0184: 0x0d5f, 0x0185: 0x0d65, + 0x0186: 0x0d6b, 0x0187: 0x0d71, 0x0188: 0x0d77, 0x0189: 0x0d7b, 0x018a: 0x0d7f, 0x018b: 0x0d83, + 0x018c: 0x0d87, 0x018d: 0x0d8b, 0x018e: 0x0d8f, 0x018f: 0x0d93, 0x0190: 0x0d97, 0x0191: 0x0d9d, + 0x0192: 0x0da3, 0x0193: 0x0da9, 0x0194: 0x0daf, 0x0195: 0x0db5, 0x0196: 0x0dbb, 0x0197: 0x0dc1, + 0x0198: 0x0dc7, 0x0199: 0x0dcd, 0x019a: 0x0dd3, 0x019b: 0x0dd9, 0x019c: 0x0ddf, 0x019d: 0x0de5, + 0x019e: 0x0deb, 0x019f: 0x0df1, 0x01a0: 0x0df7, 0x01a1: 0x0dfd, 0x01a2: 0x0e03, 0x01a3: 0x0e09, + 0x01a4: 0x0e0f, 0x01a5: 0x0e13, 0x01a6: 0x0e17, 0x01a7: 0x0e1b, 0x01a8: 0x0e1f, 0x01a9: 0x0e25, + 0x01aa: 0x0e2b, 0x01ab: 0x0e31, 0x01ac: 0x0e37, 0x01ad: 0x0e3d, 0x01ae: 0x0e43, 0x01af: 0x0e49, + 0x01b0: 0x0e4f, 0x01b1: 0x0e55, 0x01b2: 0x0e5b, 0x01b3: 0x0e5f, 0x01b4: 0x0e63, 0x01b5: 0x0e67, + 0x01b6: 0x0e6b, 0x01b7: 0x0e6f, 0x01b8: 0x0e73, 0x01b9: 0x0e77, // Block 0x7, offset 0x1c0 - 0x01cd: 0x02fb, 0x01ce: 0x02ff, 0x01cf: 0x0303, 0x01d0: 0x0307, 0x01d1: 0x030b, - 0x01d2: 0x030f, 0x01d3: 0x0313, 0x01d4: 0x0317, 0x01d5: 0x031b, 0x01d6: 0x0321, 0x01d7: 0x0327, - 0x01d8: 0x032d, 0x01d9: 0x0333, 0x01da: 0x0339, 0x01db: 0x033f, 0x01dc: 0x0345, - 0x01de: 0x034b, 0x01df: 0x0351, 0x01e0: 0x0357, 0x01e1: 0x035d, 0x01e2: 0x0363, 0x01e3: 0x0368, - 0x01e6: 0x036d, 0x01e7: 0x0371, 0x01e8: 0x0375, 0x01e9: 0x0379, - 0x01ea: 0x037d, 0x01eb: 0x0381, 0x01ec: 0x0385, 0x01ed: 0x038b, 0x01ee: 0x0391, 0x01ef: 0x0396, - 0x01f0: 0x039b, 0x01f4: 0x03a8, 0x01f5: 0x03ac, - 0x01f8: 0x03b0, 0x01f9: 0x03b4, 0x01fa: 0x03b8, 0x01fb: 0x03be, - 0x01fc: 0x03c4, 0x01fd: 0x03c9, 0x01fe: 0x03ce, 0x01ff: 0x03d3, + 0x01c0: 0x0e7b, 0x01c1: 0x0e80, 0x01c2: 0x0e85, 0x01c3: 0x0e8c, 0x01c4: 0x0e93, 0x01c5: 0x0e9a, + 0x01c6: 0x0ea1, 0x01c7: 0x0ea8, 0x01c8: 0x0eaf, 0x01c9: 0x0eb4, 0x01ca: 0x0eb9, 0x01cb: 0x0ec0, + 0x01cc: 0x0ec7, 0x01cd: 0x0ece, 0x01ce: 0x0ed5, 0x01cf: 0x0edc, 0x01d0: 0x0ee3, 0x01d1: 0x0ee8, + 0x01d2: 0x0eed, 0x01d3: 0x0ef4, 0x01d4: 0x0efb, 0x01d5: 0x0f02, + 0x01d8: 0x0f09, 0x01d9: 0x0f0e, 0x01da: 0x0f13, 0x01db: 0x0f1a, 0x01dc: 0x0f21, 0x01dd: 0x0f28, + 0x01e0: 0x0f2f, 0x01e1: 0x0f34, 0x01e2: 0x0f39, 0x01e3: 0x0f40, + 0x01e4: 0x0f47, 0x01e5: 0x0f4e, 0x01e6: 0x0f55, 0x01e7: 0x0f5c, 0x01e8: 0x0f63, 0x01e9: 0x0f68, + 0x01ea: 0x0f6d, 0x01eb: 0x0f74, 0x01ec: 0x0f7b, 0x01ed: 0x0f82, 0x01ee: 0x0f89, 0x01ef: 0x0f90, + 0x01f0: 0x0f97, 0x01f1: 0x0f9c, 0x01f2: 0x0fa1, 0x01f3: 0x0fa8, 0x01f4: 0x0faf, 0x01f5: 0x0fb6, + 0x01f6: 0x0fbd, 0x01f7: 0x0fc4, 0x01f8: 0x0fcb, 0x01f9: 0x0fd0, 0x01fa: 0x0fd5, 0x01fb: 0x0fdc, + 0x01fc: 0x0fe3, 0x01fd: 0x0fea, 0x01fe: 0x0ff1, 0x01ff: 0x0ff8, // Block 0x8, offset 0x200 - 0x0200: 0x03d8, 0x0201: 0x03dc, 0x0202: 0x03e0, 0x0203: 0x03e4, 0x0204: 0x03e8, 0x0205: 0x03ec, - 0x0206: 0x03f0, 0x0207: 0x03f4, 0x0208: 0x03f8, 0x0209: 0x03fc, 0x020a: 0x0400, 0x020b: 0x0404, - 0x020c: 0x0408, 0x020d: 0x040c, 0x020e: 0x0410, 0x020f: 0x0414, 0x0210: 0x0418, 0x0211: 0x041c, - 0x0212: 0x0420, 0x0213: 0x0424, 0x0214: 0x0428, 0x0215: 0x042c, 0x0216: 0x0430, 0x0217: 0x0434, - 0x0218: 0x0438, 0x0219: 0x043c, 0x021a: 0x0440, 0x021b: 0x0444, - 0x021e: 0x0448, 0x021f: 0x044c, - 0x0226: 0x0450, 0x0227: 0x0454, 0x0228: 0x0458, 0x0229: 0x045c, - 0x022a: 0x0460, 0x022b: 0x0466, 0x022c: 0x046c, 0x022d: 0x0472, 0x022e: 0x0478, 0x022f: 0x047c, - 0x0230: 0x0480, 0x0231: 0x0486, 0x0232: 0x048c, 0x0233: 0x0490, + 0x0200: 0x0fff, 0x0201: 0x1004, 0x0202: 0x1009, 0x0203: 0x1010, 0x0204: 0x1017, 0x0205: 0x101e, + 0x0208: 0x1025, 0x0209: 0x102a, 0x020a: 0x102f, 0x020b: 0x1036, + 0x020c: 0x103d, 0x020d: 0x1044, 0x0210: 0x104b, 0x0211: 0x1050, + 0x0212: 0x1055, 0x0213: 0x105c, 0x0214: 0x1063, 0x0215: 0x106a, 0x0216: 0x1071, 0x0217: 0x1078, + 0x0219: 0x107f, 0x021b: 0x1084, 0x021d: 0x108b, + 0x021f: 0x1092, 0x0220: 0x1099, 0x0221: 0x109e, 0x0222: 0x10a3, 0x0223: 0x10aa, + 0x0224: 0x10b1, 0x0225: 0x10b8, 0x0226: 0x10bf, 0x0227: 0x10c6, 0x0228: 0x10cd, 0x0229: 0x10d2, + 0x022a: 0x10d7, 0x022b: 0x10de, 0x022c: 0x10e5, 0x022d: 0x10ec, 0x022e: 0x10f3, 0x022f: 0x10fa, + 0x0230: 0x1101, 0x0231: 0x0525, 0x0232: 0x1106, 0x0233: 0x052a, 0x0234: 0x110b, 0x0235: 0x052f, + 0x0236: 0x1110, 0x0237: 0x0534, 0x0238: 0x1115, 0x0239: 0x054a, 0x023a: 0x111a, 0x023b: 0x054f, + 0x023c: 0x111f, 0x023d: 0x0554, // Block 0x9, offset 0x240 - 0x0240: 0x04cc, 0x0241: 0x04cf, 0x0243: 0x04d2, 0x0244: 0x04d5, - 0x0274: 0x04da, - 0x027e: 0x04e1, + 0x0240: 0x1124, 0x0241: 0x112b, 0x0242: 0x1132, 0x0243: 0x113b, 0x0244: 0x1144, 0x0245: 0x114d, + 0x0246: 0x1156, 0x0247: 0x115f, 0x0248: 0x1168, 0x0249: 0x116f, 0x024a: 0x1176, 0x024b: 0x117f, + 0x024c: 0x1188, 0x024d: 0x1191, 0x024e: 0x119a, 0x024f: 0x11a3, 0x0250: 0x11ac, 0x0251: 0x11b3, + 0x0252: 0x11ba, 0x0253: 0x11c3, 0x0254: 0x11cc, 0x0255: 0x11d5, 0x0256: 0x11de, 0x0257: 0x11e7, + 0x0258: 0x11f0, 0x0259: 0x11f7, 0x025a: 0x11fe, 0x025b: 0x1207, 0x025c: 0x1210, 0x025d: 0x1219, + 0x025e: 0x1222, 0x025f: 0x122b, 0x0260: 0x1234, 0x0261: 0x123b, 0x0262: 0x1242, 0x0263: 0x124b, + 0x0264: 0x1254, 0x0265: 0x125d, 0x0266: 0x1266, 0x0267: 0x126f, 0x0268: 0x1278, 0x0269: 0x127f, + 0x026a: 0x1286, 0x026b: 0x128f, 0x026c: 0x1298, 0x026d: 0x12a1, 0x026e: 0x12aa, 0x026f: 0x12b3, + 0x0270: 0x12bc, 0x0271: 0x12c1, 0x0272: 0x12c6, 0x0273: 0x12cd, 0x0274: 0x12d2, + 0x0276: 0x12d9, 0x0277: 0x12de, 0x0278: 0x12e5, 0x0279: 0x12ea, 0x027a: 0x12ef, 0x027b: 0x04ee, + 0x027c: 0x12f4, 0x027e: 0x12fd, // Block 0xa, offset 0x280 - 0x0285: 0x04e3, - 0x0286: 0x04ee, 0x0287: 0x04f3, 0x0288: 0x04f6, 0x0289: 0x04fb, 0x028a: 0x0500, - 0x028c: 0x0505, 0x028e: 0x050a, 0x028f: 0x050f, 0x0290: 0x0514, - 0x02aa: 0x051b, 0x02ab: 0x0520, 0x02ac: 0x0525, 0x02ad: 0x052a, 0x02ae: 0x052f, 0x02af: 0x0534, - 0x02b0: 0x0539, + 0x0281: 0x1304, 0x0282: 0x130f, 0x0283: 0x1316, 0x0284: 0x131b, + 0x0286: 0x1322, 0x0287: 0x1327, 0x0288: 0x132e, 0x0289: 0x04f6, 0x028a: 0x1333, 0x028b: 0x04fb, + 0x028c: 0x1338, 0x028d: 0x133d, 0x028e: 0x1349, 0x028f: 0x1355, 0x0290: 0x1361, 0x0291: 0x1366, + 0x0292: 0x136b, 0x0293: 0x0514, 0x0296: 0x1372, 0x0297: 0x1377, + 0x0298: 0x137e, 0x0299: 0x1383, 0x029a: 0x1388, 0x029b: 0x0500, 0x029d: 0x138d, + 0x029e: 0x1399, 0x029f: 0x13a5, 0x02a0: 0x13b1, 0x02a1: 0x13b6, 0x02a2: 0x13bb, 0x02a3: 0x0539, + 0x02a4: 0x13c2, 0x02a5: 0x13c7, 0x02a6: 0x13cc, 0x02a7: 0x13d1, 0x02a8: 0x13d8, 0x02a9: 0x13dd, + 0x02aa: 0x13e2, 0x02ab: 0x050a, 0x02ac: 0x13e7, 0x02ad: 0x13ec, 0x02ae: 0x04e3, 0x02af: 0x13f7, + 0x02b2: 0x13f9, 0x02b3: 0x1400, 0x02b4: 0x1405, + 0x02b6: 0x140c, 0x02b7: 0x1411, 0x02b8: 0x1418, 0x02b9: 0x0505, 0x02ba: 0x141d, 0x02bb: 0x050f, + 0x02bc: 0x1422, 0x02bd: 0x1427, // Block 0xb, offset 0x2c0 - 0x02ca: 0x0540, 0x02cb: 0x0545, - 0x02cc: 0x054a, 0x02cd: 0x054f, 0x02ce: 0x0554, - 0x02d3: 0x0562, 0x02d4: 0x0567, + 0x02cc: 0x1b8a, 0x02ce: 0x1b91, 0x02d0: 0x1b98, + 0x02d2: 0x1b9f, 0x02d4: 0x1ba6, 0x02d6: 0x1bad, + 0x02d8: 0x1bb4, 0x02da: 0x1bbb, 0x02dc: 0x1bc2, + 0x02de: 0x1bc9, 0x02e0: 0x1bd0, 0x02e2: 0x1bd7, + 0x02e5: 0x1bde, 0x02e7: 0x1be5, 0x02e9: 0x1bec, + 0x02f0: 0x1bf3, 0x02f1: 0x1bfa, 0x02f3: 0x1c01, 0x02f4: 0x1c08, + 0x02f6: 0x1c0f, 0x02f7: 0x1c16, 0x02f9: 0x1c1d, 0x02fa: 0x1c24, + 0x02fc: 0x1c2b, 0x02fd: 0x1c32, // Block 0xc, offset 0x300 - 0x0300: 0x0584, 0x0301: 0x0589, 0x0303: 0x058e, - 0x0307: 0x0593, - 0x030c: 0x0598, 0x030d: 0x059d, 0x030e: 0x05a2, - 0x0319: 0x05a7, - 0x0339: 0x05ac, + 0x0300: 0x2fce, 0x0301: 0x2fd2, 0x0302: 0x2fd6, 0x0303: 0x2fda, 0x0304: 0x2fde, 0x0305: 0x2fe2, + 0x0306: 0x2fe6, 0x0307: 0x2fea, 0x0308: 0x2fee, 0x0309: 0x2eed, 0x030a: 0x2ff2, 0x030b: 0x2ef1, + 0x030c: 0x2ff6, 0x030d: 0x2ffa, 0x030e: 0x2ffe, 0x030f: 0x3002, 0x0310: 0x3006, 0x0311: 0x2e6d, + 0x0312: 0x2b15, 0x0313: 0x300a, 0x0314: 0x300e, 0x0315: 0x195a, 0x0316: 0x2c25, 0x0317: 0x2d71, + 0x0318: 0x3012, 0x0319: 0x3016, 0x031a: 0x2f0d, 0x031b: 0x301a, 0x031c: 0x2f11, 0x031d: 0x301e, + 0x031e: 0x3022, 0x031f: 0x3026, 0x0320: 0x2e75, 0x0321: 0x302a, 0x0322: 0x302e, 0x0323: 0x3032, + 0x0324: 0x3036, 0x0325: 0x303a, 0x0326: 0x2e79, 0x0327: 0x303e, 0x0328: 0x3042, 0x0329: 0x3046, + 0x032a: 0x304a, 0x032b: 0x304e, 0x032c: 0x3052, 0x032d: 0x2f41, 0x032e: 0x3056, 0x032f: 0x305a, + 0x0330: 0x2cb1, 0x0331: 0x305e, 0x0332: 0x2f51, 0x0333: 0x3062, 0x0334: 0x3066, 0x0335: 0x306a, + 0x0336: 0x306e, 0x0337: 0x3072, 0x0338: 0x2f65, 0x0339: 0x3076, 0x033a: 0x2e99, 0x033b: 0x307a, + 0x033c: 0x2f69, 0x033d: 0x2bd9, 0x033e: 0x307e, 0x033f: 0x2f6d, // Block 0xd, offset 0x340 - 0x0350: 0x05b1, 0x0351: 0x05b6, - 0x0353: 0x05bb, 0x0357: 0x05c0, - 0x035c: 0x05c5, 0x035d: 0x05ca, - 0x035e: 0x05cf, - 0x0376: 0x05d4, 0x0377: 0x05d9, + 0x0340: 0x3082, 0x0341: 0x2f75, 0x0342: 0x3086, 0x0343: 0x308a, 0x0344: 0x308e, 0x0345: 0x3092, + 0x0346: 0x3096, 0x0347: 0x2f7d, 0x0348: 0x2e8d, 0x0349: 0x309a, 0x034a: 0x2f81, 0x034b: 0x309e, + 0x034c: 0x2f85, 0x034d: 0x30a2, 0x034e: 0x1b76, 0x034f: 0x30a6, 0x0350: 0x30ab, 0x0351: 0x30b0, + 0x0352: 0x30b5, 0x0353: 0x30b9, 0x0354: 0x30bd, 0x0355: 0x30c1, 0x0356: 0x30c6, 0x0357: 0x30cb, + 0x0358: 0x30d0, 0x0359: 0x30d4, // Block 0xe, offset 0x380 - 0x0381: 0x05de, 0x0382: 0x05e3, - 0x0390: 0x05e8, 0x0391: 0x05ed, - 0x0392: 0x05f2, 0x0393: 0x05f7, 0x0396: 0x05fc, 0x0397: 0x0601, - 0x039a: 0x0606, 0x039b: 0x060b, 0x039c: 0x0610, 0x039d: 0x0615, - 0x039e: 0x061a, 0x039f: 0x061f, 0x03a2: 0x0624, 0x03a3: 0x0629, - 0x03a4: 0x062e, 0x03a5: 0x0633, 0x03a6: 0x0638, 0x03a7: 0x063d, - 0x03aa: 0x0642, 0x03ab: 0x0647, 0x03ac: 0x064c, 0x03ad: 0x0651, 0x03ae: 0x0656, 0x03af: 0x065b, - 0x03b0: 0x0660, 0x03b1: 0x0665, 0x03b2: 0x066a, 0x03b3: 0x066f, 0x03b4: 0x0674, 0x03b5: 0x0679, - 0x03b8: 0x067e, 0x03b9: 0x0683, + 0x0380: 0x3d23, 0x0381: 0x3d27, 0x0382: 0x3d2b, 0x0383: 0x3d2f, 0x0384: 0x3d34, 0x0385: 0x2eb5, + 0x0386: 0x3d38, 0x0387: 0x3d3c, 0x0388: 0x3d40, 0x0389: 0x3d44, 0x038a: 0x2eb9, 0x038b: 0x3d48, + 0x038c: 0x3d4c, 0x038d: 0x3d50, 0x038e: 0x2ebd, 0x038f: 0x3d55, 0x0390: 0x3d59, 0x0391: 0x3d5d, + 0x0392: 0x3d61, 0x0393: 0x3d66, 0x0394: 0x3d6a, 0x0395: 0x3c71, 0x0396: 0x3d6e, 0x0397: 0x3d73, + 0x0398: 0x3d77, 0x0399: 0x3d7b, 0x039a: 0x3d7f, 0x039b: 0x2f9a, 0x039c: 0x3d83, 0x039d: 0x1866, + 0x039e: 0x3d88, 0x039f: 0x3d8c, 0x03a0: 0x3d90, 0x03a1: 0x3d94, 0x03a2: 0x3cb9, 0x03a3: 0x3d98, + 0x03a4: 0x3d9c, 0x03a5: 0x2fae, 0x03a6: 0x2ec1, 0x03a7: 0x2ec5, 0x03a8: 0x2fb2, 0x03a9: 0x3da0, + 0x03aa: 0x3da4, 0x03ab: 0x2bf1, 0x03ac: 0x3da8, 0x03ad: 0x2ec9, 0x03ae: 0x3dac, 0x03af: 0x3db0, + 0x03b0: 0x3db4, 0x03b1: 0x3db8, 0x03b2: 0x3db8, 0x03b3: 0x3db8, 0x03b4: 0x3dbc, 0x03b5: 0x3dc1, + 0x03b6: 0x3dc5, 0x03b7: 0x3dc9, 0x03b8: 0x3dcd, 0x03b9: 0x3dd2, 0x03ba: 0x3dd6, 0x03bb: 0x3dda, + 0x03bc: 0x3dde, 0x03bd: 0x3de2, 0x03be: 0x3de6, 0x03bf: 0x3dea, // Block 0xf, offset 0x3c0 - 0x03e2: 0x068d, 0x03e3: 0x0692, - 0x03e4: 0x0697, 0x03e5: 0x069c, 0x03e6: 0x06a1, + 0x03c0: 0x3dee, 0x03c1: 0x3df2, 0x03c2: 0x3df6, 0x03c3: 0x3dfa, 0x03c4: 0x3dfe, 0x03c5: 0x3e02, + 0x03c6: 0x3e02, 0x03c7: 0x2fba, 0x03c8: 0x3e06, 0x03c9: 0x3e0a, 0x03ca: 0x3e0e, 0x03cb: 0x3e12, + 0x03cc: 0x2ed1, 0x03cd: 0x3e16, 0x03ce: 0x3e1a, 0x03cf: 0x3e1e, 0x03d0: 0x2e39, 0x03d1: 0x3e22, + 0x03d2: 0x3e26, 0x03d3: 0x3e2a, 0x03d4: 0x3e2e, 0x03d5: 0x3e32, 0x03d6: 0x3e36, 0x03d7: 0x3e3a, + 0x03d8: 0x3e3e, 0x03d9: 0x3e42, 0x03da: 0x3e47, 0x03db: 0x3e4b, 0x03dc: 0x3e4f, 0x03dd: 0x3c55, + 0x03de: 0x3e53, 0x03df: 0x3e57, 0x03e0: 0x3e5b, 0x03e1: 0x3e60, 0x03e2: 0x3e65, 0x03e3: 0x3e69, + 0x03e4: 0x3e6d, 0x03e5: 0x3e71, 0x03e6: 0x3e75, 0x03e7: 0x3e79, 0x03e8: 0x3e7d, 0x03e9: 0x3e81, + 0x03ea: 0x3e85, 0x03eb: 0x3e85, 0x03ec: 0x3e89, 0x03ed: 0x3e8e, 0x03ee: 0x3e92, 0x03ef: 0x2be1, + 0x03f0: 0x3e96, 0x03f1: 0x3e9a, 0x03f2: 0x3e9f, 0x03f3: 0x3ea3, 0x03f4: 0x3ea7, 0x03f5: 0x18ce, + 0x03f6: 0x3eab, 0x03f7: 0x3eaf, 0x03f8: 0x18d6, 0x03f9: 0x3eb3, 0x03fa: 0x3eb7, 0x03fb: 0x3ebb, + 0x03fc: 0x3ec0, 0x03fd: 0x3ec4, 0x03fe: 0x3ec9, 0x03ff: 0x3ecd, // Block 0x10, offset 0x400 - 0x0400: 0x06ba, 0x0402: 0x06bf, - 0x0413: 0x06c4, + 0x0400: 0x3ed1, 0x0401: 0x3ed5, 0x0402: 0x3ed9, 0x0403: 0x3edd, 0x0404: 0x3ee1, 0x0405: 0x3ee5, + 0x0406: 0x3ee9, 0x0407: 0x3eed, 0x0408: 0x3ef1, 0x0409: 0x3ef5, 0x040a: 0x3efa, 0x040b: 0x3efe, + 0x040c: 0x3f02, 0x040d: 0x3f06, 0x040e: 0x2b11, 0x040f: 0x3f0a, 0x0410: 0x18fe, 0x0411: 0x3f0f, + 0x0412: 0x3f0f, 0x0413: 0x3f14, 0x0414: 0x3f18, 0x0415: 0x3f18, 0x0416: 0x3f1c, 0x0417: 0x3f20, + 0x0418: 0x3f25, 0x0419: 0x3f2a, 0x041a: 0x3f2e, 0x041b: 0x3f32, 0x041c: 0x3f36, 0x041d: 0x3f3a, + 0x041e: 0x3f3e, 0x041f: 0x3f42, 0x0420: 0x3f46, 0x0421: 0x3f4a, 0x0422: 0x3f4e, 0x0423: 0x2ee5, + 0x0424: 0x3f52, 0x0425: 0x3f57, 0x0426: 0x3f5b, 0x0427: 0x3f5f, 0x0428: 0x2fea, 0x0429: 0x3f5f, + 0x042a: 0x3f63, 0x042b: 0x2eed, 0x042c: 0x3f67, 0x042d: 0x3f6b, 0x042e: 0x3f6f, 0x042f: 0x3f73, + 0x0430: 0x2ef1, 0x0431: 0x2aa5, 0x0432: 0x3f77, 0x0433: 0x3f7b, 0x0434: 0x3f7f, 0x0435: 0x3f83, + 0x0436: 0x3f87, 0x0437: 0x3f8b, 0x0438: 0x3f8f, 0x0439: 0x3f94, 0x043a: 0x3f98, 0x043b: 0x3f9c, + 0x043c: 0x3fa0, 0x043d: 0x3fa4, 0x043e: 0x3fa8, 0x043f: 0x3fad, // Block 0x11, offset 0x440 - 0x0469: 0x06c9, - 0x0471: 0x06d0, 0x0474: 0x06d7, + 0x0440: 0x3fb1, 0x0441: 0x3fb5, 0x0442: 0x3fb9, 0x0443: 0x3fbd, 0x0444: 0x3fc1, 0x0445: 0x3fc5, + 0x0446: 0x3fc9, 0x0447: 0x3fcd, 0x0448: 0x2ef5, 0x0449: 0x3fd1, 0x044a: 0x3fd5, 0x044b: 0x3fda, + 0x044c: 0x3fde, 0x044d: 0x3fe2, 0x044e: 0x3fe6, 0x044f: 0x2efd, 0x0450: 0x3fea, 0x0451: 0x3fee, + 0x0452: 0x3ff2, 0x0453: 0x3ff6, 0x0454: 0x3ffa, 0x0455: 0x3ffe, 0x0456: 0x4002, 0x0457: 0x4006, + 0x0458: 0x2b15, 0x0459: 0x300a, 0x045a: 0x400a, 0x045b: 0x400e, 0x045c: 0x4012, 0x045d: 0x4016, + 0x045e: 0x401b, 0x045f: 0x401f, 0x0460: 0x4023, 0x0461: 0x4027, 0x0462: 0x2f01, 0x0463: 0x402b, + 0x0464: 0x4030, 0x0465: 0x4034, 0x0466: 0x4038, 0x0467: 0x30b5, 0x0468: 0x403c, 0x0469: 0x4040, + 0x046a: 0x4044, 0x046b: 0x4048, 0x046c: 0x404c, 0x046d: 0x4051, 0x046e: 0x4055, 0x046f: 0x4059, + 0x0470: 0x405d, 0x0471: 0x4062, 0x0472: 0x4066, 0x0473: 0x406a, 0x0474: 0x406e, 0x0475: 0x2c25, + 0x0476: 0x4072, 0x0477: 0x4076, 0x0478: 0x407b, 0x0479: 0x4080, 0x047a: 0x4085, 0x047b: 0x4089, + 0x047c: 0x408e, 0x047d: 0x4092, 0x047e: 0x4096, 0x047f: 0x409a, // Block 0x12, offset 0x480 - 0x0498: 0x06de, 0x0499: 0x06e5, 0x049a: 0x06ec, 0x049b: 0x06f3, 0x049c: 0x06fa, 0x049d: 0x0701, - 0x049e: 0x0708, 0x049f: 0x070f, + 0x0480: 0x409e, 0x0481: 0x2f05, 0x0482: 0x2d71, 0x0483: 0x40a2, 0x0484: 0x40a6, 0x0485: 0x40aa, + 0x0486: 0x40ae, 0x0487: 0x40b3, 0x0488: 0x40b7, 0x0489: 0x40bb, 0x048a: 0x40bf, 0x048b: 0x3016, + 0x048c: 0x40c3, 0x048d: 0x40c7, 0x048e: 0x40cc, 0x048f: 0x40d0, 0x0490: 0x40d4, 0x0491: 0x40d9, + 0x0492: 0x40de, 0x0493: 0x40e2, 0x0494: 0x301a, 0x0495: 0x40e6, 0x0496: 0x40ea, 0x0497: 0x40ee, + 0x0498: 0x40f2, 0x0499: 0x40f6, 0x049a: 0x40fa, 0x049b: 0x40fe, 0x049c: 0x4103, 0x049d: 0x4107, + 0x049e: 0x410c, 0x049f: 0x4110, 0x04a0: 0x4115, 0x04a1: 0x3022, 0x04a2: 0x4119, 0x04a3: 0x411d, + 0x04a4: 0x4122, 0x04a5: 0x4126, 0x04a6: 0x412a, 0x04a7: 0x412f, 0x04a8: 0x4134, 0x04a9: 0x4138, + 0x04aa: 0x413c, 0x04ab: 0x4140, 0x04ac: 0x4144, 0x04ad: 0x4144, 0x04ae: 0x4148, 0x04af: 0x414c, + 0x04b0: 0x302a, 0x04b1: 0x4150, 0x04b2: 0x4154, 0x04b3: 0x4158, 0x04b4: 0x415c, 0x04b5: 0x4160, + 0x04b6: 0x4165, 0x04b7: 0x4169, 0x04b8: 0x2bed, 0x04b9: 0x416e, 0x04ba: 0x4173, 0x04bb: 0x4177, + 0x04bc: 0x417c, 0x04bd: 0x4181, 0x04be: 0x4186, 0x04bf: 0x418a, // Block 0x13, offset 0x4c0 - 0x04cb: 0x0716, - 0x04cc: 0x071d, - 0x04dc: 0x0724, 0x04dd: 0x072b, - 0x04df: 0x0732, + 0x04c0: 0x3042, 0x04c1: 0x418e, 0x04c2: 0x4193, 0x04c3: 0x4198, 0x04c4: 0x419d, 0x04c5: 0x41a2, + 0x04c6: 0x41a6, 0x04c7: 0x41a6, 0x04c8: 0x3046, 0x04c9: 0x30bd, 0x04ca: 0x41aa, 0x04cb: 0x41ae, + 0x04cc: 0x41b2, 0x04cd: 0x41b6, 0x04ce: 0x41bb, 0x04cf: 0x2b59, 0x04d0: 0x304e, 0x04d1: 0x41bf, + 0x04d2: 0x41c3, 0x04d3: 0x2f2d, 0x04d4: 0x41c8, 0x04d5: 0x41cd, 0x04d6: 0x2e89, 0x04d7: 0x41d2, + 0x04d8: 0x41d6, 0x04d9: 0x2f39, 0x04da: 0x41da, 0x04db: 0x41de, 0x04dc: 0x41e2, 0x04dd: 0x41e7, + 0x04de: 0x41e7, 0x04df: 0x41ec, 0x04e0: 0x41f0, 0x04e1: 0x41f4, 0x04e2: 0x41f9, 0x04e3: 0x41fd, + 0x04e4: 0x4201, 0x04e5: 0x4205, 0x04e6: 0x420a, 0x04e7: 0x420e, 0x04e8: 0x4212, 0x04e9: 0x4216, + 0x04ea: 0x421a, 0x04eb: 0x421e, 0x04ec: 0x4223, 0x04ed: 0x4227, 0x04ee: 0x422b, 0x04ef: 0x422f, + 0x04f0: 0x4233, 0x04f1: 0x4237, 0x04f2: 0x423b, 0x04f3: 0x4240, 0x04f4: 0x4245, 0x04f5: 0x4249, + 0x04f6: 0x424e, 0x04f7: 0x4252, 0x04f8: 0x4257, 0x04f9: 0x425b, 0x04fa: 0x2f51, 0x04fb: 0x425f, + 0x04fc: 0x4264, 0x04fd: 0x4269, 0x04fe: 0x426d, 0x04ff: 0x4272, // Block 0x14, offset 0x500 - 0x0533: 0x0739, - 0x0536: 0x0740, + 0x0500: 0x4276, 0x0501: 0x427b, 0x0502: 0x427f, 0x0503: 0x4283, 0x0504: 0x4287, 0x0505: 0x428b, + 0x0506: 0x428f, 0x0507: 0x4293, 0x0508: 0x4298, 0x0509: 0x429d, 0x050a: 0x42a2, 0x050b: 0x3f14, + 0x050c: 0x42a7, 0x050d: 0x42ab, 0x050e: 0x42af, 0x050f: 0x42b3, 0x0510: 0x42b7, 0x0511: 0x42bb, + 0x0512: 0x42bf, 0x0513: 0x42c3, 0x0514: 0x42c7, 0x0515: 0x42cb, 0x0516: 0x42cf, 0x0517: 0x42d3, + 0x0518: 0x2c31, 0x0519: 0x42d8, 0x051a: 0x42dc, 0x051b: 0x42e0, 0x051c: 0x42e4, 0x051d: 0x42e8, + 0x051e: 0x42ec, 0x051f: 0x2f5d, 0x0520: 0x42f0, 0x0521: 0x42f4, 0x0522: 0x42f8, 0x0523: 0x42fc, + 0x0524: 0x4300, 0x0525: 0x4305, 0x0526: 0x430a, 0x0527: 0x430f, 0x0528: 0x4313, 0x0529: 0x4317, + 0x052a: 0x431b, 0x052b: 0x431f, 0x052c: 0x4324, 0x052d: 0x4328, 0x052e: 0x432d, 0x052f: 0x4331, + 0x0530: 0x4335, 0x0531: 0x433a, 0x0532: 0x433f, 0x0533: 0x4343, 0x0534: 0x2b45, 0x0535: 0x4347, + 0x0536: 0x434b, 0x0537: 0x434f, 0x0538: 0x4353, 0x0539: 0x4357, 0x053a: 0x435b, 0x053b: 0x306a, + 0x053c: 0x435f, 0x053d: 0x4363, 0x053e: 0x4367, 0x053f: 0x436b, // Block 0x15, offset 0x540 - 0x0559: 0x0747, 0x055a: 0x074e, 0x055b: 0x0755, - 0x055e: 0x075c, - // Block 0x16, offset 0x580 - 0x0588: 0x0763, 0x058b: 0x076a, - 0x058c: 0x0771, - 0x059c: 0x0778, 0x059d: 0x077f, - // Block 0x17, offset 0x5c0 - 0x05d4: 0x0786, - // Block 0x18, offset 0x600 - 0x060a: 0x078d, 0x060b: 0x0794, - 0x060c: 0x079b, - // Block 0x19, offset 0x640 - 0x0648: 0x07a2, - // Block 0x1a, offset 0x680 - 0x0680: 0x07a9, - 0x0687: 0x07b0, 0x0688: 0x07b7, 0x068a: 0x07be, 0x068b: 0x07c5, - // Block 0x1b, offset 0x6c0 - 0x06ca: 0x07cf, 0x06cb: 0x07d6, - 0x06cc: 0x07dd, - // Block 0x1c, offset 0x700 - 0x071a: 0x07e4, 0x071c: 0x07eb, 0x071d: 0x07f2, - 0x071e: 0x07fc, - // Block 0x1d, offset 0x740 - 0x0743: 0x0823, - 0x074d: 0x082a, - 0x0752: 0x0831, 0x0757: 0x0838, - 0x075c: 0x083f, - 0x0769: 0x0846, - 0x0773: 0x084d, 0x0775: 0x0854, - 0x0776: 0x085b, 0x0778: 0x086c, - // Block 0x1e, offset 0x780 - 0x0781: 0x087d, - 0x0793: 0x0884, - 0x079d: 0x088b, - 0x07a2: 0x0892, - 0x07a7: 0x0899, - 0x07ac: 0x08a0, - 0x07b9: 0x08a7, - // Block 0x1f, offset 0x7c0 - 0x07e6: 0x08ae, - // Block 0x20, offset 0x800 - 0x0806: 0x08b9, 0x0808: 0x08c0, 0x080a: 0x08c7, - 0x080c: 0x08ce, 0x080e: 0x08d5, - 0x0812: 0x08dc, - 0x083b: 0x08e3, - 0x083d: 0x08ea, - // Block 0x21, offset 0x840 - 0x0840: 0x08f1, 0x0841: 0x08f8, 0x0843: 0x08ff, - // Block 0x22, offset 0x880 - 0x0880: 0x09ea, 0x0881: 0x09ee, 0x0882: 0x09f2, 0x0883: 0x09f6, 0x0884: 0x09fa, 0x0885: 0x09fe, - 0x0886: 0x0a02, 0x0887: 0x0a06, 0x0888: 0x0a0a, 0x0889: 0x0a10, 0x088a: 0x0a16, 0x088b: 0x0a1a, - 0x088c: 0x0a1e, 0x088d: 0x0a22, 0x088e: 0x0a26, 0x088f: 0x0a2a, 0x0890: 0x0a2e, 0x0891: 0x0a32, - 0x0892: 0x0a36, 0x0893: 0x0a3a, 0x0894: 0x0a3e, 0x0895: 0x0a44, 0x0896: 0x0a4a, 0x0897: 0x0a50, - 0x0898: 0x0a56, 0x0899: 0x0a5a, 0x089a: 0x0a5e, 0x089b: 0x0a62, 0x089c: 0x0a66, 0x089d: 0x0a6c, - 0x089e: 0x0a72, 0x089f: 0x0a76, 0x08a0: 0x0a7a, 0x08a1: 0x0a7e, 0x08a2: 0x0a82, 0x08a3: 0x0a86, - 0x08a4: 0x0a8a, 0x08a5: 0x0a8e, 0x08a6: 0x0a92, 0x08a7: 0x0a96, 0x08a8: 0x0a9a, 0x08a9: 0x0a9e, - 0x08aa: 0x0aa2, 0x08ab: 0x0aa6, 0x08ac: 0x0aaa, 0x08ad: 0x0aae, 0x08ae: 0x0ab2, 0x08af: 0x0ab8, - 0x08b0: 0x0abe, 0x08b1: 0x0ac2, 0x08b2: 0x0ac6, 0x08b3: 0x0aca, 0x08b4: 0x0ace, 0x08b5: 0x0ad2, - 0x08b6: 0x0ad6, 0x08b7: 0x0ada, 0x08b8: 0x0ade, 0x08b9: 0x0ae4, 0x08ba: 0x0aea, 0x08bb: 0x0aee, - 0x08bc: 0x0af2, 0x08bd: 0x0af6, 0x08be: 0x0afa, 0x08bf: 0x0afe, - // Block 0x23, offset 0x8c0 - 0x08c0: 0x0b02, 0x08c1: 0x0b06, 0x08c2: 0x0b0a, 0x08c3: 0x0b0e, 0x08c4: 0x0b12, 0x08c5: 0x0b16, - 0x08c6: 0x0b1a, 0x08c7: 0x0b1e, 0x08c8: 0x0b22, 0x08c9: 0x0b26, 0x08ca: 0x0b2a, 0x08cb: 0x0b2e, - 0x08cc: 0x0b32, 0x08cd: 0x0b38, 0x08ce: 0x0b3e, 0x08cf: 0x0b44, 0x08d0: 0x0b4a, 0x08d1: 0x0b50, - 0x08d2: 0x0b56, 0x08d3: 0x0b5c, 0x08d4: 0x0b62, 0x08d5: 0x0b66, 0x08d6: 0x0b6a, 0x08d7: 0x0b6e, - 0x08d8: 0x0b72, 0x08d9: 0x0b76, 0x08da: 0x0b7a, 0x08db: 0x0b7e, 0x08dc: 0x0b82, 0x08dd: 0x0b88, - 0x08de: 0x0b8e, 0x08df: 0x0b92, 0x08e0: 0x0b96, 0x08e1: 0x0b9a, 0x08e2: 0x0b9e, 0x08e3: 0x0ba2, - 0x08e4: 0x0ba6, 0x08e5: 0x0bac, 0x08e6: 0x0bb2, 0x08e7: 0x0bb8, 0x08e8: 0x0bbe, 0x08e9: 0x0bc4, - 0x08ea: 0x0bca, 0x08eb: 0x0bce, 0x08ec: 0x0bd2, 0x08ed: 0x0bd6, 0x08ee: 0x0bda, 0x08ef: 0x0bde, - 0x08f0: 0x0be2, 0x08f1: 0x0be6, 0x08f2: 0x0bea, 0x08f3: 0x0bee, 0x08f4: 0x0bf2, 0x08f5: 0x0bf6, - 0x08f6: 0x0bfa, 0x08f7: 0x0bfe, 0x08f8: 0x0c02, 0x08f9: 0x0c08, 0x08fa: 0x0c0e, 0x08fb: 0x0c14, - 0x08fc: 0x0c1a, 0x08fd: 0x0c1e, 0x08fe: 0x0c22, 0x08ff: 0x0c26, - // Block 0x24, offset 0x900 - 0x0900: 0x0c2a, 0x0901: 0x0c2e, 0x0902: 0x0c32, 0x0903: 0x0c36, 0x0904: 0x0c3a, 0x0905: 0x0c3e, - 0x0906: 0x0c42, 0x0907: 0x0c46, 0x0908: 0x0c4a, 0x0909: 0x0c4e, 0x090a: 0x0c52, 0x090b: 0x0c56, - 0x090c: 0x0c5a, 0x090d: 0x0c5e, 0x090e: 0x0c62, 0x090f: 0x0c66, 0x0910: 0x0c6a, 0x0911: 0x0c6e, - 0x0912: 0x0c72, 0x0913: 0x0c76, 0x0914: 0x0c7a, 0x0915: 0x0c7e, 0x0916: 0x0c82, 0x0917: 0x0c86, - 0x0918: 0x0c8a, 0x0919: 0x0c8e, 0x091b: 0x0c96, - 0x0920: 0x0c9b, 0x0921: 0x0c9f, 0x0922: 0x0ca3, 0x0923: 0x0ca7, - 0x0924: 0x0cab, 0x0925: 0x0cb1, 0x0926: 0x0cb7, 0x0927: 0x0cbd, 0x0928: 0x0cc3, 0x0929: 0x0cc9, - 0x092a: 0x0ccf, 0x092b: 0x0cd5, 0x092c: 0x0cdb, 0x092d: 0x0ce1, 0x092e: 0x0ce7, 0x092f: 0x0ced, - 0x0930: 0x0cf3, 0x0931: 0x0cf9, 0x0932: 0x0cff, 0x0933: 0x0d05, 0x0934: 0x0d0b, 0x0935: 0x0d11, - 0x0936: 0x0d17, 0x0937: 0x0d1d, 0x0938: 0x0d23, 0x0939: 0x0d27, 0x093a: 0x0d2b, 0x093b: 0x0d2f, - 0x093c: 0x0d33, 0x093d: 0x0d37, 0x093e: 0x0d3b, 0x093f: 0x0d41, - // Block 0x25, offset 0x940 - 0x0940: 0x0d47, 0x0941: 0x0d4d, 0x0942: 0x0d53, 0x0943: 0x0d59, 0x0944: 0x0d5f, 0x0945: 0x0d65, - 0x0946: 0x0d6b, 0x0947: 0x0d71, 0x0948: 0x0d77, 0x0949: 0x0d7b, 0x094a: 0x0d7f, 0x094b: 0x0d83, - 0x094c: 0x0d87, 0x094d: 0x0d8b, 0x094e: 0x0d8f, 0x094f: 0x0d93, 0x0950: 0x0d97, 0x0951: 0x0d9d, - 0x0952: 0x0da3, 0x0953: 0x0da9, 0x0954: 0x0daf, 0x0955: 0x0db5, 0x0956: 0x0dbb, 0x0957: 0x0dc1, - 0x0958: 0x0dc7, 0x0959: 0x0dcd, 0x095a: 0x0dd3, 0x095b: 0x0dd9, 0x095c: 0x0ddf, 0x095d: 0x0de5, - 0x095e: 0x0deb, 0x095f: 0x0df1, 0x0960: 0x0df7, 0x0961: 0x0dfd, 0x0962: 0x0e03, 0x0963: 0x0e09, - 0x0964: 0x0e0f, 0x0965: 0x0e13, 0x0966: 0x0e17, 0x0967: 0x0e1b, 0x0968: 0x0e1f, 0x0969: 0x0e25, - 0x096a: 0x0e2b, 0x096b: 0x0e31, 0x096c: 0x0e37, 0x096d: 0x0e3d, 0x096e: 0x0e43, 0x096f: 0x0e49, - 0x0970: 0x0e4f, 0x0971: 0x0e55, 0x0972: 0x0e5b, 0x0973: 0x0e5f, 0x0974: 0x0e63, 0x0975: 0x0e67, - 0x0976: 0x0e6b, 0x0977: 0x0e6f, 0x0978: 0x0e73, 0x0979: 0x0e77, - // Block 0x26, offset 0x980 - 0x0980: 0x0e7b, 0x0981: 0x0e80, 0x0982: 0x0e85, 0x0983: 0x0e8c, 0x0984: 0x0e93, 0x0985: 0x0e9a, - 0x0986: 0x0ea1, 0x0987: 0x0ea8, 0x0988: 0x0eaf, 0x0989: 0x0eb4, 0x098a: 0x0eb9, 0x098b: 0x0ec0, - 0x098c: 0x0ec7, 0x098d: 0x0ece, 0x098e: 0x0ed5, 0x098f: 0x0edc, 0x0990: 0x0ee3, 0x0991: 0x0ee8, - 0x0992: 0x0eed, 0x0993: 0x0ef4, 0x0994: 0x0efb, 0x0995: 0x0f02, - 0x0998: 0x0f09, 0x0999: 0x0f0e, 0x099a: 0x0f13, 0x099b: 0x0f1a, 0x099c: 0x0f21, 0x099d: 0x0f28, - 0x09a0: 0x0f2f, 0x09a1: 0x0f34, 0x09a2: 0x0f39, 0x09a3: 0x0f40, - 0x09a4: 0x0f47, 0x09a5: 0x0f4e, 0x09a6: 0x0f55, 0x09a7: 0x0f5c, 0x09a8: 0x0f63, 0x09a9: 0x0f68, - 0x09aa: 0x0f6d, 0x09ab: 0x0f74, 0x09ac: 0x0f7b, 0x09ad: 0x0f82, 0x09ae: 0x0f89, 0x09af: 0x0f90, - 0x09b0: 0x0f97, 0x09b1: 0x0f9c, 0x09b2: 0x0fa1, 0x09b3: 0x0fa8, 0x09b4: 0x0faf, 0x09b5: 0x0fb6, - 0x09b6: 0x0fbd, 0x09b7: 0x0fc4, 0x09b8: 0x0fcb, 0x09b9: 0x0fd0, 0x09ba: 0x0fd5, 0x09bb: 0x0fdc, - 0x09bc: 0x0fe3, 0x09bd: 0x0fea, 0x09be: 0x0ff1, 0x09bf: 0x0ff8, - // Block 0x27, offset 0x9c0 - 0x09c0: 0x0fff, 0x09c1: 0x1004, 0x09c2: 0x1009, 0x09c3: 0x1010, 0x09c4: 0x1017, 0x09c5: 0x101e, - 0x09c8: 0x1025, 0x09c9: 0x102a, 0x09ca: 0x102f, 0x09cb: 0x1036, - 0x09cc: 0x103d, 0x09cd: 0x1044, 0x09d0: 0x104b, 0x09d1: 0x1050, - 0x09d2: 0x1055, 0x09d3: 0x105c, 0x09d4: 0x1063, 0x09d5: 0x106a, 0x09d6: 0x1071, 0x09d7: 0x1078, - 0x09d9: 0x107f, 0x09db: 0x1084, 0x09dd: 0x108b, - 0x09df: 0x1092, 0x09e0: 0x1099, 0x09e1: 0x109e, 0x09e2: 0x10a3, 0x09e3: 0x10aa, - 0x09e4: 0x10b1, 0x09e5: 0x10b8, 0x09e6: 0x10bf, 0x09e7: 0x10c6, 0x09e8: 0x10cd, 0x09e9: 0x10d2, - 0x09ea: 0x10d7, 0x09eb: 0x10de, 0x09ec: 0x10e5, 0x09ed: 0x10ec, 0x09ee: 0x10f3, 0x09ef: 0x10fa, - 0x09f0: 0x1101, 0x09f1: 0x0525, 0x09f2: 0x1106, 0x09f3: 0x052a, 0x09f4: 0x110b, 0x09f5: 0x052f, - 0x09f6: 0x1110, 0x09f7: 0x0534, 0x09f8: 0x1115, 0x09f9: 0x054a, 0x09fa: 0x111a, 0x09fb: 0x054f, - 0x09fc: 0x111f, 0x09fd: 0x0554, - // Block 0x28, offset 0xa00 - 0x0a00: 0x1124, 0x0a01: 0x112b, 0x0a02: 0x1132, 0x0a03: 0x113b, 0x0a04: 0x1144, 0x0a05: 0x114d, - 0x0a06: 0x1156, 0x0a07: 0x115f, 0x0a08: 0x1168, 0x0a09: 0x116f, 0x0a0a: 0x1176, 0x0a0b: 0x117f, - 0x0a0c: 0x1188, 0x0a0d: 0x1191, 0x0a0e: 0x119a, 0x0a0f: 0x11a3, 0x0a10: 0x11ac, 0x0a11: 0x11b3, - 0x0a12: 0x11ba, 0x0a13: 0x11c3, 0x0a14: 0x11cc, 0x0a15: 0x11d5, 0x0a16: 0x11de, 0x0a17: 0x11e7, - 0x0a18: 0x11f0, 0x0a19: 0x11f7, 0x0a1a: 0x11fe, 0x0a1b: 0x1207, 0x0a1c: 0x1210, 0x0a1d: 0x1219, - 0x0a1e: 0x1222, 0x0a1f: 0x122b, 0x0a20: 0x1234, 0x0a21: 0x123b, 0x0a22: 0x1242, 0x0a23: 0x124b, - 0x0a24: 0x1254, 0x0a25: 0x125d, 0x0a26: 0x1266, 0x0a27: 0x126f, 0x0a28: 0x1278, 0x0a29: 0x127f, - 0x0a2a: 0x1286, 0x0a2b: 0x128f, 0x0a2c: 0x1298, 0x0a2d: 0x12a1, 0x0a2e: 0x12aa, 0x0a2f: 0x12b3, - 0x0a30: 0x12bc, 0x0a31: 0x12c1, 0x0a32: 0x12c6, 0x0a33: 0x12cd, 0x0a34: 0x12d2, - 0x0a36: 0x12d9, 0x0a37: 0x12de, 0x0a38: 0x12e5, 0x0a39: 0x12ea, 0x0a3a: 0x12ef, 0x0a3b: 0x04ee, - 0x0a3c: 0x12f4, 0x0a3e: 0x12fd, - // Block 0x29, offset 0xa40 - 0x0a41: 0x1304, 0x0a42: 0x130f, 0x0a43: 0x1316, 0x0a44: 0x131b, - 0x0a46: 0x1322, 0x0a47: 0x1327, 0x0a48: 0x132e, 0x0a49: 0x04f6, 0x0a4a: 0x1333, 0x0a4b: 0x04fb, - 0x0a4c: 0x1338, 0x0a4d: 0x133d, 0x0a4e: 0x1349, 0x0a4f: 0x1355, 0x0a50: 0x1361, 0x0a51: 0x1366, - 0x0a52: 0x136b, 0x0a53: 0x0514, 0x0a56: 0x1372, 0x0a57: 0x1377, - 0x0a58: 0x137e, 0x0a59: 0x1383, 0x0a5a: 0x1388, 0x0a5b: 0x0500, 0x0a5d: 0x138d, - 0x0a5e: 0x1399, 0x0a5f: 0x13a5, 0x0a60: 0x13b1, 0x0a61: 0x13b6, 0x0a62: 0x13bb, 0x0a63: 0x0539, - 0x0a64: 0x13c2, 0x0a65: 0x13c7, 0x0a66: 0x13cc, 0x0a67: 0x13d1, 0x0a68: 0x13d8, 0x0a69: 0x13dd, - 0x0a6a: 0x13e2, 0x0a6b: 0x050a, 0x0a6c: 0x13e7, 0x0a6d: 0x13ec, 0x0a6e: 0x04e3, 0x0a6f: 0x13f7, - 0x0a72: 0x13f9, 0x0a73: 0x1400, 0x0a74: 0x1405, - 0x0a76: 0x140c, 0x0a77: 0x1411, 0x0a78: 0x1418, 0x0a79: 0x0505, 0x0a7a: 0x141d, 0x0a7b: 0x050f, - 0x0a7c: 0x1422, 0x0a7d: 0x1427, - // Block 0x2a, offset 0xa80 - 0x0a80: 0x142e, 0x0a81: 0x1432, - // Block 0x2b, offset 0xac0 - 0x0ae6: 0x14d6, - 0x0aea: 0x091c, 0x0aeb: 0x0046, - // Block 0x2c, offset 0xb00 - 0x0b1a: 0x159f, 0x0b1b: 0x15a5, - 0x0b2e: 0x15ab, - // Block 0x2d, offset 0xb40 - 0x0b4d: 0x15b1, 0x0b4e: 0x15b7, 0x0b4f: 0x15bd, - // Block 0x2e, offset 0xb80 - 0x0b84: 0x15c3, - 0x0b89: 0x15c9, - 0x0b8c: 0x15cf, - 0x0ba4: 0x15d5, 0x0ba6: 0x15db, - // Block 0x2f, offset 0xbc0 - 0x0bc1: 0x1603, 0x0bc4: 0x1609, - 0x0bc7: 0x160f, 0x0bc9: 0x1615, - 0x0be0: 0x161b, 0x0be2: 0x161f, - 0x0bed: 0x1625, 0x0bee: 0x162b, 0x0bef: 0x162f, - 0x0bf0: 0x1633, 0x0bf1: 0x1639, 0x0bf4: 0x163f, 0x0bf5: 0x1645, - 0x0bf8: 0x164b, 0x0bf9: 0x1651, - // Block 0x30, offset 0xc00 - 0x0c00: 0x1657, 0x0c01: 0x165d, 0x0c04: 0x1663, 0x0c05: 0x1669, - 0x0c08: 0x166f, 0x0c09: 0x1675, - 0x0c2c: 0x167b, 0x0c2d: 0x1681, 0x0c2e: 0x1687, 0x0c2f: 0x168d, - // Block 0x31, offset 0xc40 - 0x0c60: 0x1693, 0x0c61: 0x1699, 0x0c62: 0x169f, 0x0c63: 0x16a5, - 0x0c6a: 0x16ab, 0x0c6b: 0x16b1, 0x0c6c: 0x16b7, 0x0c6d: 0x16bd, - // Block 0x32, offset 0xc80 - 0x0ca9: 0x16c3, - 0x0caa: 0x16c7, - // Block 0x33, offset 0xcc0 - 0x0cdc: 0x1814, - // Block 0x34, offset 0xd00 - 0x0d0c: 0x1b8a, 0x0d0e: 0x1b91, 0x0d10: 0x1b98, - 0x0d12: 0x1b9f, 0x0d14: 0x1ba6, 0x0d16: 0x1bad, - 0x0d18: 0x1bb4, 0x0d1a: 0x1bbb, 0x0d1c: 0x1bc2, - 0x0d1e: 0x1bc9, 0x0d20: 0x1bd0, 0x0d22: 0x1bd7, - 0x0d25: 0x1bde, 0x0d27: 0x1be5, 0x0d29: 0x1bec, - 0x0d30: 0x1bf3, 0x0d31: 0x1bfa, 0x0d33: 0x1c01, 0x0d34: 0x1c08, - 0x0d36: 0x1c0f, 0x0d37: 0x1c16, 0x0d39: 0x1c1d, 0x0d3a: 0x1c24, - 0x0d3c: 0x1c2b, 0x0d3d: 0x1c32, - // Block 0x35, offset 0xd40 - 0x0d54: 0x1c39, - 0x0d5e: 0x1c4a, - 0x0d6c: 0x1c58, 0x0d6e: 0x1c5f, - 0x0d70: 0x1c66, 0x0d72: 0x1c6d, 0x0d74: 0x1c74, - 0x0d76: 0x1c7b, 0x0d78: 0x1c82, 0x0d7a: 0x1c89, - 0x0d7c: 0x1c90, 0x0d7e: 0x1c97, - // Block 0x36, offset 0xd80 - 0x0d80: 0x1c9e, 0x0d82: 0x1ca5, 0x0d85: 0x1cac, - 0x0d87: 0x1cb3, 0x0d89: 0x1cba, - 0x0d90: 0x1cc1, 0x0d91: 0x1cc8, - 0x0d93: 0x1ccf, 0x0d94: 0x1cd6, 0x0d96: 0x1cdd, 0x0d97: 0x1ce4, - 0x0d99: 0x1ceb, 0x0d9a: 0x1cf2, 0x0d9c: 0x1cf9, 0x0d9d: 0x1d00, - 0x0db4: 0x1d07, - 0x0db7: 0x1d0e, 0x0db8: 0x1d15, 0x0db9: 0x1d1c, 0x0dba: 0x1d23, - 0x0dbe: 0x1d2a, - // Block 0x37, offset 0xdc0 - 0x0dc0: 0x2a81, 0x0dc1: 0x2a85, 0x0dc2: 0x1a9e, 0x0dc3: 0x2a89, 0x0dc4: 0x2a8d, 0x0dc5: 0x2a91, - 0x0dc6: 0x2a95, 0x0dc7: 0x1b76, 0x0dc8: 0x1b76, 0x0dc9: 0x2a99, 0x0dca: 0x1abe, 0x0dcb: 0x2a9d, - 0x0dcc: 0x2aa1, 0x0dcd: 0x2aa5, 0x0dce: 0x2aa9, 0x0dcf: 0x2aad, 0x0dd0: 0x2ab1, 0x0dd1: 0x2ab5, - 0x0dd2: 0x2ab9, 0x0dd3: 0x2abd, 0x0dd4: 0x2ac1, 0x0dd5: 0x2ac5, 0x0dd6: 0x2ac9, 0x0dd7: 0x2acd, - 0x0dd8: 0x2ad1, 0x0dd9: 0x2ad5, 0x0dda: 0x2ad9, 0x0ddb: 0x2add, 0x0ddc: 0x2ae1, 0x0ddd: 0x2ae5, - 0x0dde: 0x2ae9, 0x0ddf: 0x2aed, 0x0de0: 0x2af1, 0x0de1: 0x2af5, 0x0de2: 0x2af9, 0x0de3: 0x2afd, - 0x0de4: 0x2b01, 0x0de5: 0x2b05, 0x0de6: 0x2b09, 0x0de7: 0x2b0d, 0x0de8: 0x2b11, 0x0de9: 0x2b15, - 0x0dea: 0x2b19, 0x0deb: 0x2b1d, 0x0dec: 0x2b21, 0x0ded: 0x2b25, 0x0dee: 0x2b29, 0x0def: 0x2b2d, - 0x0df0: 0x2b31, 0x0df1: 0x2b35, 0x0df2: 0x2b39, 0x0df3: 0x2b3d, 0x0df4: 0x1a16, 0x0df5: 0x2b41, - 0x0df6: 0x2b45, 0x0df7: 0x2b49, 0x0df8: 0x2b4d, 0x0df9: 0x2b51, 0x0dfa: 0x2b55, 0x0dfb: 0x2b59, - 0x0dfc: 0x2b5d, 0x0dfd: 0x2b61, 0x0dfe: 0x2b65, 0x0dff: 0x2b69, - // Block 0x38, offset 0xe00 - 0x0e00: 0x1b3a, 0x0e01: 0x2b6d, 0x0e02: 0x2b71, 0x0e03: 0x2b75, 0x0e04: 0x2b79, 0x0e05: 0x2b7d, - 0x0e06: 0x2b81, 0x0e07: 0x2b85, 0x0e08: 0x2b89, 0x0e09: 0x2b8d, 0x0e0a: 0x2b91, 0x0e0b: 0x2b95, - 0x0e0c: 0x2b99, 0x0e0d: 0x2b9d, 0x0e0e: 0x2ba1, 0x0e0f: 0x2ba5, 0x0e10: 0x2ba9, 0x0e11: 0x2bad, - 0x0e12: 0x2bb1, 0x0e13: 0x2bb5, 0x0e14: 0x2bb9, 0x0e15: 0x2bbd, 0x0e16: 0x2bc1, 0x0e17: 0x2bc5, - 0x0e18: 0x2bc9, 0x0e19: 0x2bcd, 0x0e1a: 0x2bd1, 0x0e1b: 0x2bd5, 0x0e1c: 0x2ac1, 0x0e1d: 0x2bd9, - 0x0e1e: 0x2bdd, 0x0e1f: 0x2be1, 0x0e20: 0x2be5, 0x0e21: 0x2be9, 0x0e22: 0x2bed, 0x0e23: 0x2bf1, - 0x0e24: 0x2bf5, 0x0e25: 0x2bf9, 0x0e26: 0x2bfd, 0x0e27: 0x2c01, 0x0e28: 0x2c05, 0x0e29: 0x2c09, - 0x0e2a: 0x2c0d, 0x0e2b: 0x2c11, 0x0e2c: 0x2c15, 0x0e2d: 0x2c19, 0x0e2e: 0x2c1d, 0x0e2f: 0x2c21, - 0x0e30: 0x2c25, 0x0e31: 0x1aa6, 0x0e32: 0x2c29, 0x0e33: 0x2c2d, 0x0e34: 0x2c31, 0x0e35: 0x2c35, - 0x0e36: 0x2c39, 0x0e37: 0x2c3d, 0x0e38: 0x2c41, 0x0e39: 0x2c45, 0x0e3a: 0x2c49, 0x0e3b: 0x2c4d, - 0x0e3c: 0x2c51, 0x0e3d: 0x2c55, 0x0e3e: 0x2c59, 0x0e3f: 0x2c5d, - // Block 0x39, offset 0xe40 - 0x0e40: 0x2c61, 0x0e41: 0x18ba, 0x0e42: 0x2c65, 0x0e43: 0x2c69, 0x0e44: 0x2c6d, 0x0e45: 0x2c71, - 0x0e46: 0x2c75, 0x0e47: 0x2c79, 0x0e48: 0x2c7d, 0x0e49: 0x2c81, 0x0e4a: 0x186e, 0x0e4b: 0x2c85, - 0x0e4c: 0x2c89, 0x0e4d: 0x2c8d, 0x0e4e: 0x2c91, 0x0e4f: 0x2c95, 0x0e50: 0x2c99, 0x0e51: 0x2c9d, - 0x0e52: 0x2ca1, 0x0e53: 0x2ca5, 0x0e54: 0x2ca9, 0x0e55: 0x2cad, 0x0e56: 0x2cb1, 0x0e57: 0x2cb5, - 0x0e58: 0x2cb9, 0x0e59: 0x2cbd, 0x0e5a: 0x2cc1, 0x0e5b: 0x2cc5, 0x0e5c: 0x2cc9, 0x0e5d: 0x2ccd, - 0x0e5e: 0x2cd1, 0x0e5f: 0x2cd5, 0x0e60: 0x2cd9, 0x0e61: 0x2c21, 0x0e62: 0x2cdd, 0x0e63: 0x2ce1, - 0x0e64: 0x2ce5, 0x0e65: 0x2ce9, 0x0e66: 0x2ced, 0x0e67: 0x2cf1, 0x0e68: 0x2cf5, 0x0e69: 0x2cf9, - 0x0e6a: 0x2be1, 0x0e6b: 0x2cfd, 0x0e6c: 0x2d01, 0x0e6d: 0x2d05, 0x0e6e: 0x2d09, 0x0e6f: 0x2d0d, - 0x0e70: 0x2d11, 0x0e71: 0x2d15, 0x0e72: 0x2d19, 0x0e73: 0x2d1d, 0x0e74: 0x2d21, 0x0e75: 0x2d25, - 0x0e76: 0x2d29, 0x0e77: 0x2d2d, 0x0e78: 0x2d31, 0x0e79: 0x2d35, 0x0e7a: 0x2d39, 0x0e7b: 0x2d3d, - 0x0e7c: 0x2d41, 0x0e7d: 0x2d45, 0x0e7e: 0x2d49, 0x0e7f: 0x2ac1, - // Block 0x3a, offset 0xe80 - 0x0e80: 0x2d4d, 0x0e81: 0x2d51, 0x0e82: 0x2d55, 0x0e83: 0x2d59, 0x0e84: 0x1b72, 0x0e85: 0x2d5d, - 0x0e86: 0x2d61, 0x0e87: 0x2d65, 0x0e88: 0x2d69, 0x0e89: 0x2d6d, 0x0e8a: 0x2d71, 0x0e8b: 0x2d75, - 0x0e8c: 0x2d79, 0x0e8d: 0x2d7d, 0x0e8e: 0x2d81, 0x0e8f: 0x2d85, 0x0e90: 0x2d89, 0x0e91: 0x2173, - 0x0e92: 0x2d8d, 0x0e93: 0x2d91, 0x0e94: 0x2d95, 0x0e95: 0x2d99, 0x0e96: 0x2d9d, 0x0e97: 0x2da1, - 0x0e98: 0x2da5, 0x0e99: 0x2da9, 0x0e9a: 0x2dad, 0x0e9b: 0x2be9, 0x0e9c: 0x2db1, 0x0e9d: 0x2db5, - 0x0e9e: 0x2db9, 0x0e9f: 0x2dbd, 0x0ea0: 0x2dc1, 0x0ea1: 0x2dc5, 0x0ea2: 0x2dc9, 0x0ea3: 0x2dcd, - 0x0ea4: 0x2dd1, 0x0ea5: 0x2dd5, 0x0ea6: 0x2dd9, 0x0ea7: 0x2ddd, 0x0ea8: 0x2de1, 0x0ea9: 0x1aba, - 0x0eaa: 0x2de5, 0x0eab: 0x2de9, 0x0eac: 0x2ded, 0x0ead: 0x2df1, 0x0eae: 0x2df5, 0x0eaf: 0x2df9, - 0x0eb0: 0x2dfd, 0x0eb1: 0x2e01, 0x0eb2: 0x2e05, 0x0eb3: 0x2e09, 0x0eb4: 0x2e0d, 0x0eb5: 0x2e11, - 0x0eb6: 0x2e15, 0x0eb7: 0x19f6, 0x0eb8: 0x2e19, 0x0eb9: 0x2e1d, 0x0eba: 0x2e21, 0x0ebb: 0x2e25, - 0x0ebc: 0x2e29, 0x0ebd: 0x2e2d, 0x0ebe: 0x2e31, 0x0ebf: 0x2e35, - // Block 0x3b, offset 0xec0 - 0x0ec0: 0x2e39, 0x0ec1: 0x2e3d, 0x0ec2: 0x2e41, 0x0ec3: 0x2e45, 0x0ec4: 0x2e49, 0x0ec5: 0x2e4d, - 0x0ec6: 0x2e51, 0x0ec7: 0x2e55, 0x0ec8: 0x1a62, 0x0ec9: 0x2e59, 0x0eca: 0x1a6e, 0x0ecb: 0x2e5d, - 0x0ecc: 0x2e61, 0x0ecd: 0x2e65, 0x0ed0: 0x2e69, - 0x0ed2: 0x2e6d, 0x0ed5: 0x2e71, 0x0ed6: 0x2e75, 0x0ed7: 0x2e79, - 0x0ed8: 0x2e7d, 0x0ed9: 0x2e81, 0x0eda: 0x2e85, 0x0edb: 0x2e89, 0x0edc: 0x2e8d, 0x0edd: 0x2e91, - 0x0ede: 0x1a12, 0x0ee0: 0x2e95, 0x0ee2: 0x2e99, - 0x0ee5: 0x2e9d, 0x0ee6: 0x2ea1, - 0x0eea: 0x2ea5, 0x0eeb: 0x2ea9, 0x0eec: 0x2ead, 0x0eed: 0x2eb1, - 0x0ef0: 0x2eb5, 0x0ef1: 0x2eb9, 0x0ef2: 0x2ebd, 0x0ef3: 0x2ec1, 0x0ef4: 0x2ec5, 0x0ef5: 0x2ec9, - 0x0ef6: 0x2ecd, 0x0ef7: 0x2ed1, 0x0ef8: 0x2ed5, 0x0ef9: 0x2ed9, 0x0efa: 0x2edd, 0x0efb: 0x2ee1, - 0x0efc: 0x18d6, 0x0efd: 0x2ee5, 0x0efe: 0x2ee9, 0x0eff: 0x2eed, - // Block 0x3c, offset 0xf00 - 0x0f00: 0x2ef1, 0x0f01: 0x2ef5, 0x0f02: 0x2ef9, 0x0f03: 0x2efd, 0x0f04: 0x2f01, 0x0f05: 0x2f05, - 0x0f06: 0x2f09, 0x0f07: 0x2f0d, 0x0f08: 0x2f11, 0x0f09: 0x2f15, 0x0f0a: 0x2f19, 0x0f0b: 0x2f1d, - 0x0f0c: 0x2187, 0x0f0d: 0x2f21, 0x0f0e: 0x2f25, 0x0f0f: 0x2f29, 0x0f10: 0x2f2d, 0x0f11: 0x2197, - 0x0f12: 0x2f31, 0x0f13: 0x2f35, 0x0f14: 0x2f39, 0x0f15: 0x2f3d, 0x0f16: 0x2f41, 0x0f17: 0x2cb1, - 0x0f18: 0x2f45, 0x0f19: 0x2f49, 0x0f1a: 0x2f4d, 0x0f1b: 0x2f51, 0x0f1c: 0x2f55, 0x0f1d: 0x2f59, - 0x0f1e: 0x2f59, 0x0f1f: 0x2f5d, 0x0f20: 0x2f61, 0x0f21: 0x2f65, 0x0f22: 0x2f69, 0x0f23: 0x2f6d, - 0x0f24: 0x2f71, 0x0f25: 0x2f75, 0x0f26: 0x2f79, 0x0f27: 0x2e9d, 0x0f28: 0x2f7d, 0x0f29: 0x2f81, - 0x0f2a: 0x2f85, 0x0f2b: 0x2f89, 0x0f2c: 0x2f8d, 0x0f2d: 0x2f92, - 0x0f30: 0x2f96, 0x0f31: 0x2f9a, 0x0f32: 0x2f9e, 0x0f33: 0x2fa2, 0x0f34: 0x2fa6, 0x0f35: 0x2faa, - 0x0f36: 0x2fae, 0x0f37: 0x2fb2, 0x0f38: 0x2ecd, 0x0f39: 0x2fb6, 0x0f3a: 0x2fba, 0x0f3b: 0x2fbe, - 0x0f3c: 0x2e69, 0x0f3d: 0x2fc2, 0x0f3e: 0x2fc6, 0x0f3f: 0x2fca, - // Block 0x3d, offset 0xf40 - 0x0f40: 0x2fce, 0x0f41: 0x2fd2, 0x0f42: 0x2fd6, 0x0f43: 0x2fda, 0x0f44: 0x2fde, 0x0f45: 0x2fe2, - 0x0f46: 0x2fe6, 0x0f47: 0x2fea, 0x0f48: 0x2fee, 0x0f49: 0x2eed, 0x0f4a: 0x2ff2, 0x0f4b: 0x2ef1, - 0x0f4c: 0x2ff6, 0x0f4d: 0x2ffa, 0x0f4e: 0x2ffe, 0x0f4f: 0x3002, 0x0f50: 0x3006, 0x0f51: 0x2e6d, - 0x0f52: 0x2b15, 0x0f53: 0x300a, 0x0f54: 0x300e, 0x0f55: 0x195a, 0x0f56: 0x2c25, 0x0f57: 0x2d71, - 0x0f58: 0x3012, 0x0f59: 0x3016, 0x0f5a: 0x2f0d, 0x0f5b: 0x301a, 0x0f5c: 0x2f11, 0x0f5d: 0x301e, - 0x0f5e: 0x3022, 0x0f5f: 0x3026, 0x0f60: 0x2e75, 0x0f61: 0x302a, 0x0f62: 0x302e, 0x0f63: 0x3032, - 0x0f64: 0x3036, 0x0f65: 0x303a, 0x0f66: 0x2e79, 0x0f67: 0x303e, 0x0f68: 0x3042, 0x0f69: 0x3046, - 0x0f6a: 0x304a, 0x0f6b: 0x304e, 0x0f6c: 0x3052, 0x0f6d: 0x2f41, 0x0f6e: 0x3056, 0x0f6f: 0x305a, - 0x0f70: 0x2cb1, 0x0f71: 0x305e, 0x0f72: 0x2f51, 0x0f73: 0x3062, 0x0f74: 0x3066, 0x0f75: 0x306a, - 0x0f76: 0x306e, 0x0f77: 0x3072, 0x0f78: 0x2f65, 0x0f79: 0x3076, 0x0f7a: 0x2e99, 0x0f7b: 0x307a, - 0x0f7c: 0x2f69, 0x0f7d: 0x2bd9, 0x0f7e: 0x307e, 0x0f7f: 0x2f6d, - // Block 0x3e, offset 0xf80 - 0x0f80: 0x3082, 0x0f81: 0x2f75, 0x0f82: 0x3086, 0x0f83: 0x308a, 0x0f84: 0x308e, 0x0f85: 0x3092, - 0x0f86: 0x3096, 0x0f87: 0x2f7d, 0x0f88: 0x2e8d, 0x0f89: 0x309a, 0x0f8a: 0x2f81, 0x0f8b: 0x309e, - 0x0f8c: 0x2f85, 0x0f8d: 0x30a2, 0x0f8e: 0x1b76, 0x0f8f: 0x30a6, 0x0f90: 0x30ab, 0x0f91: 0x30b0, - 0x0f92: 0x30b5, 0x0f93: 0x30b9, 0x0f94: 0x30bd, 0x0f95: 0x30c1, 0x0f96: 0x30c6, 0x0f97: 0x30cb, - 0x0f98: 0x30d0, 0x0f99: 0x30d4, - // Block 0x3f, offset 0xfc0 - 0x0fdd: 0x3105, - 0x0fdf: 0x310a, - 0x0fea: 0x3124, 0x0feb: 0x3129, 0x0fec: 0x312e, 0x0fed: 0x3135, 0x0fee: 0x313c, 0x0fef: 0x3141, - 0x0ff0: 0x3146, 0x0ff1: 0x314b, 0x0ff2: 0x3150, 0x0ff3: 0x3155, 0x0ff4: 0x315a, 0x0ff5: 0x315f, - 0x0ff6: 0x3164, 0x0ff8: 0x3169, 0x0ff9: 0x316e, 0x0ffa: 0x3173, 0x0ffb: 0x3178, - 0x0ffc: 0x317d, 0x0ffe: 0x3182, - // Block 0x40, offset 0x1000 - 0x1000: 0x3187, 0x1001: 0x318c, 0x1003: 0x3191, 0x1004: 0x3196, - 0x1006: 0x319b, 0x1007: 0x31a0, 0x1008: 0x31a5, 0x1009: 0x31aa, 0x100a: 0x31af, 0x100b: 0x31b4, - 0x100c: 0x31b9, 0x100d: 0x31be, 0x100e: 0x31c3, - // Block 0x41, offset 0x1040 - 0x105a: 0x3a73, 0x105c: 0x3a7c, - 0x106b: 0x3a85, - // Block 0x42, offset 0x1080 - 0x109e: 0x3a8e, 0x109f: 0x3a97, 0x10a0: 0x3aa0, 0x10a1: 0x3aad, 0x10a2: 0x3aba, 0x10a3: 0x3ac7, - 0x10a4: 0x3ad4, - // Block 0x43, offset 0x10c0 - 0x10fb: 0x3ae1, - 0x10fc: 0x3aea, 0x10fd: 0x3af3, 0x10fe: 0x3b00, 0x10ff: 0x3b0d, - // Block 0x44, offset 0x1100 - 0x1100: 0x3b1a, - // Block 0x45, offset 0x1140 - 0x1140: 0x3d23, 0x1141: 0x3d27, 0x1142: 0x3d2b, 0x1143: 0x3d2f, 0x1144: 0x3d34, 0x1145: 0x2eb5, - 0x1146: 0x3d38, 0x1147: 0x3d3c, 0x1148: 0x3d40, 0x1149: 0x3d44, 0x114a: 0x2eb9, 0x114b: 0x3d48, - 0x114c: 0x3d4c, 0x114d: 0x3d50, 0x114e: 0x2ebd, 0x114f: 0x3d55, 0x1150: 0x3d59, 0x1151: 0x3d5d, - 0x1152: 0x3d61, 0x1153: 0x3d66, 0x1154: 0x3d6a, 0x1155: 0x3c71, 0x1156: 0x3d6e, 0x1157: 0x3d73, - 0x1158: 0x3d77, 0x1159: 0x3d7b, 0x115a: 0x3d7f, 0x115b: 0x2f9a, 0x115c: 0x3d83, 0x115d: 0x1866, - 0x115e: 0x3d88, 0x115f: 0x3d8c, 0x1160: 0x3d90, 0x1161: 0x3d94, 0x1162: 0x3cb9, 0x1163: 0x3d98, - 0x1164: 0x3d9c, 0x1165: 0x2fae, 0x1166: 0x2ec1, 0x1167: 0x2ec5, 0x1168: 0x2fb2, 0x1169: 0x3da0, - 0x116a: 0x3da4, 0x116b: 0x2bf1, 0x116c: 0x3da8, 0x116d: 0x2ec9, 0x116e: 0x3dac, 0x116f: 0x3db0, - 0x1170: 0x3db4, 0x1171: 0x3db8, 0x1172: 0x3db8, 0x1173: 0x3db8, 0x1174: 0x3dbc, 0x1175: 0x3dc1, - 0x1176: 0x3dc5, 0x1177: 0x3dc9, 0x1178: 0x3dcd, 0x1179: 0x3dd2, 0x117a: 0x3dd6, 0x117b: 0x3dda, - 0x117c: 0x3dde, 0x117d: 0x3de2, 0x117e: 0x3de6, 0x117f: 0x3dea, - // Block 0x46, offset 0x1180 - 0x1180: 0x3dee, 0x1181: 0x3df2, 0x1182: 0x3df6, 0x1183: 0x3dfa, 0x1184: 0x3dfe, 0x1185: 0x3e02, - 0x1186: 0x3e02, 0x1187: 0x2fba, 0x1188: 0x3e06, 0x1189: 0x3e0a, 0x118a: 0x3e0e, 0x118b: 0x3e12, - 0x118c: 0x2ed1, 0x118d: 0x3e16, 0x118e: 0x3e1a, 0x118f: 0x3e1e, 0x1190: 0x2e39, 0x1191: 0x3e22, - 0x1192: 0x3e26, 0x1193: 0x3e2a, 0x1194: 0x3e2e, 0x1195: 0x3e32, 0x1196: 0x3e36, 0x1197: 0x3e3a, - 0x1198: 0x3e3e, 0x1199: 0x3e42, 0x119a: 0x3e47, 0x119b: 0x3e4b, 0x119c: 0x3e4f, 0x119d: 0x3c55, - 0x119e: 0x3e53, 0x119f: 0x3e57, 0x11a0: 0x3e5b, 0x11a1: 0x3e60, 0x11a2: 0x3e65, 0x11a3: 0x3e69, - 0x11a4: 0x3e6d, 0x11a5: 0x3e71, 0x11a6: 0x3e75, 0x11a7: 0x3e79, 0x11a8: 0x3e7d, 0x11a9: 0x3e81, - 0x11aa: 0x3e85, 0x11ab: 0x3e85, 0x11ac: 0x3e89, 0x11ad: 0x3e8e, 0x11ae: 0x3e92, 0x11af: 0x2be1, - 0x11b0: 0x3e96, 0x11b1: 0x3e9a, 0x11b2: 0x3e9f, 0x11b3: 0x3ea3, 0x11b4: 0x3ea7, 0x11b5: 0x18ce, - 0x11b6: 0x3eab, 0x11b7: 0x3eaf, 0x11b8: 0x18d6, 0x11b9: 0x3eb3, 0x11ba: 0x3eb7, 0x11bb: 0x3ebb, - 0x11bc: 0x3ec0, 0x11bd: 0x3ec4, 0x11be: 0x3ec9, 0x11bf: 0x3ecd, - // Block 0x47, offset 0x11c0 - 0x11c0: 0x3ed1, 0x11c1: 0x3ed5, 0x11c2: 0x3ed9, 0x11c3: 0x3edd, 0x11c4: 0x3ee1, 0x11c5: 0x3ee5, - 0x11c6: 0x3ee9, 0x11c7: 0x3eed, 0x11c8: 0x3ef1, 0x11c9: 0x3ef5, 0x11ca: 0x3efa, 0x11cb: 0x3efe, - 0x11cc: 0x3f02, 0x11cd: 0x3f06, 0x11ce: 0x2b11, 0x11cf: 0x3f0a, 0x11d0: 0x18fe, 0x11d1: 0x3f0f, - 0x11d2: 0x3f0f, 0x11d3: 0x3f14, 0x11d4: 0x3f18, 0x11d5: 0x3f18, 0x11d6: 0x3f1c, 0x11d7: 0x3f20, - 0x11d8: 0x3f25, 0x11d9: 0x3f2a, 0x11da: 0x3f2e, 0x11db: 0x3f32, 0x11dc: 0x3f36, 0x11dd: 0x3f3a, - 0x11de: 0x3f3e, 0x11df: 0x3f42, 0x11e0: 0x3f46, 0x11e1: 0x3f4a, 0x11e2: 0x3f4e, 0x11e3: 0x2ee5, - 0x11e4: 0x3f52, 0x11e5: 0x3f57, 0x11e6: 0x3f5b, 0x11e7: 0x3f5f, 0x11e8: 0x2fea, 0x11e9: 0x3f5f, - 0x11ea: 0x3f63, 0x11eb: 0x2eed, 0x11ec: 0x3f67, 0x11ed: 0x3f6b, 0x11ee: 0x3f6f, 0x11ef: 0x3f73, - 0x11f0: 0x2ef1, 0x11f1: 0x2aa5, 0x11f2: 0x3f77, 0x11f3: 0x3f7b, 0x11f4: 0x3f7f, 0x11f5: 0x3f83, - 0x11f6: 0x3f87, 0x11f7: 0x3f8b, 0x11f8: 0x3f8f, 0x11f9: 0x3f94, 0x11fa: 0x3f98, 0x11fb: 0x3f9c, - 0x11fc: 0x3fa0, 0x11fd: 0x3fa4, 0x11fe: 0x3fa8, 0x11ff: 0x3fad, - // Block 0x48, offset 0x1200 - 0x1200: 0x3fb1, 0x1201: 0x3fb5, 0x1202: 0x3fb9, 0x1203: 0x3fbd, 0x1204: 0x3fc1, 0x1205: 0x3fc5, - 0x1206: 0x3fc9, 0x1207: 0x3fcd, 0x1208: 0x2ef5, 0x1209: 0x3fd1, 0x120a: 0x3fd5, 0x120b: 0x3fda, - 0x120c: 0x3fde, 0x120d: 0x3fe2, 0x120e: 0x3fe6, 0x120f: 0x2efd, 0x1210: 0x3fea, 0x1211: 0x3fee, - 0x1212: 0x3ff2, 0x1213: 0x3ff6, 0x1214: 0x3ffa, 0x1215: 0x3ffe, 0x1216: 0x4002, 0x1217: 0x4006, - 0x1218: 0x2b15, 0x1219: 0x300a, 0x121a: 0x400a, 0x121b: 0x400e, 0x121c: 0x4012, 0x121d: 0x4016, - 0x121e: 0x401b, 0x121f: 0x401f, 0x1220: 0x4023, 0x1221: 0x4027, 0x1222: 0x2f01, 0x1223: 0x402b, - 0x1224: 0x4030, 0x1225: 0x4034, 0x1226: 0x4038, 0x1227: 0x30b5, 0x1228: 0x403c, 0x1229: 0x4040, - 0x122a: 0x4044, 0x122b: 0x4048, 0x122c: 0x404c, 0x122d: 0x4051, 0x122e: 0x4055, 0x122f: 0x4059, - 0x1230: 0x405d, 0x1231: 0x4062, 0x1232: 0x4066, 0x1233: 0x406a, 0x1234: 0x406e, 0x1235: 0x2c25, - 0x1236: 0x4072, 0x1237: 0x4076, 0x1238: 0x407b, 0x1239: 0x4080, 0x123a: 0x4085, 0x123b: 0x4089, - 0x123c: 0x408e, 0x123d: 0x4092, 0x123e: 0x4096, 0x123f: 0x409a, - // Block 0x49, offset 0x1240 - 0x1240: 0x409e, 0x1241: 0x2f05, 0x1242: 0x2d71, 0x1243: 0x40a2, 0x1244: 0x40a6, 0x1245: 0x40aa, - 0x1246: 0x40ae, 0x1247: 0x40b3, 0x1248: 0x40b7, 0x1249: 0x40bb, 0x124a: 0x40bf, 0x124b: 0x3016, - 0x124c: 0x40c3, 0x124d: 0x40c7, 0x124e: 0x40cc, 0x124f: 0x40d0, 0x1250: 0x40d4, 0x1251: 0x40d9, - 0x1252: 0x40de, 0x1253: 0x40e2, 0x1254: 0x301a, 0x1255: 0x40e6, 0x1256: 0x40ea, 0x1257: 0x40ee, - 0x1258: 0x40f2, 0x1259: 0x40f6, 0x125a: 0x40fa, 0x125b: 0x40fe, 0x125c: 0x4103, 0x125d: 0x4107, - 0x125e: 0x410c, 0x125f: 0x4110, 0x1260: 0x4115, 0x1261: 0x3022, 0x1262: 0x4119, 0x1263: 0x411d, - 0x1264: 0x4122, 0x1265: 0x4126, 0x1266: 0x412a, 0x1267: 0x412f, 0x1268: 0x4134, 0x1269: 0x4138, - 0x126a: 0x413c, 0x126b: 0x4140, 0x126c: 0x4144, 0x126d: 0x4144, 0x126e: 0x4148, 0x126f: 0x414c, - 0x1270: 0x302a, 0x1271: 0x4150, 0x1272: 0x4154, 0x1273: 0x4158, 0x1274: 0x415c, 0x1275: 0x4160, - 0x1276: 0x4165, 0x1277: 0x4169, 0x1278: 0x2bed, 0x1279: 0x416e, 0x127a: 0x4173, 0x127b: 0x4177, - 0x127c: 0x417c, 0x127d: 0x4181, 0x127e: 0x4186, 0x127f: 0x418a, - // Block 0x4a, offset 0x1280 - 0x1280: 0x3042, 0x1281: 0x418e, 0x1282: 0x4193, 0x1283: 0x4198, 0x1284: 0x419d, 0x1285: 0x41a2, - 0x1286: 0x41a6, 0x1287: 0x41a6, 0x1288: 0x3046, 0x1289: 0x30bd, 0x128a: 0x41aa, 0x128b: 0x41ae, - 0x128c: 0x41b2, 0x128d: 0x41b6, 0x128e: 0x41bb, 0x128f: 0x2b59, 0x1290: 0x304e, 0x1291: 0x41bf, - 0x1292: 0x41c3, 0x1293: 0x2f2d, 0x1294: 0x41c8, 0x1295: 0x41cd, 0x1296: 0x2e89, 0x1297: 0x41d2, - 0x1298: 0x41d6, 0x1299: 0x2f39, 0x129a: 0x41da, 0x129b: 0x41de, 0x129c: 0x41e2, 0x129d: 0x41e7, - 0x129e: 0x41e7, 0x129f: 0x41ec, 0x12a0: 0x41f0, 0x12a1: 0x41f4, 0x12a2: 0x41f9, 0x12a3: 0x41fd, - 0x12a4: 0x4201, 0x12a5: 0x4205, 0x12a6: 0x420a, 0x12a7: 0x420e, 0x12a8: 0x4212, 0x12a9: 0x4216, - 0x12aa: 0x421a, 0x12ab: 0x421e, 0x12ac: 0x4223, 0x12ad: 0x4227, 0x12ae: 0x422b, 0x12af: 0x422f, - 0x12b0: 0x4233, 0x12b1: 0x4237, 0x12b2: 0x423b, 0x12b3: 0x4240, 0x12b4: 0x4245, 0x12b5: 0x4249, - 0x12b6: 0x424e, 0x12b7: 0x4252, 0x12b8: 0x4257, 0x12b9: 0x425b, 0x12ba: 0x2f51, 0x12bb: 0x425f, - 0x12bc: 0x4264, 0x12bd: 0x4269, 0x12be: 0x426d, 0x12bf: 0x4272, - // Block 0x4b, offset 0x12c0 - 0x12c0: 0x4276, 0x12c1: 0x427b, 0x12c2: 0x427f, 0x12c3: 0x4283, 0x12c4: 0x4287, 0x12c5: 0x428b, - 0x12c6: 0x428f, 0x12c7: 0x4293, 0x12c8: 0x4298, 0x12c9: 0x429d, 0x12ca: 0x42a2, 0x12cb: 0x3f14, - 0x12cc: 0x42a7, 0x12cd: 0x42ab, 0x12ce: 0x42af, 0x12cf: 0x42b3, 0x12d0: 0x42b7, 0x12d1: 0x42bb, - 0x12d2: 0x42bf, 0x12d3: 0x42c3, 0x12d4: 0x42c7, 0x12d5: 0x42cb, 0x12d6: 0x42cf, 0x12d7: 0x42d3, - 0x12d8: 0x2c31, 0x12d9: 0x42d8, 0x12da: 0x42dc, 0x12db: 0x42e0, 0x12dc: 0x42e4, 0x12dd: 0x42e8, - 0x12de: 0x42ec, 0x12df: 0x2f5d, 0x12e0: 0x42f0, 0x12e1: 0x42f4, 0x12e2: 0x42f8, 0x12e3: 0x42fc, - 0x12e4: 0x4300, 0x12e5: 0x4305, 0x12e6: 0x430a, 0x12e7: 0x430f, 0x12e8: 0x4313, 0x12e9: 0x4317, - 0x12ea: 0x431b, 0x12eb: 0x431f, 0x12ec: 0x4324, 0x12ed: 0x4328, 0x12ee: 0x432d, 0x12ef: 0x4331, - 0x12f0: 0x4335, 0x12f1: 0x433a, 0x12f2: 0x433f, 0x12f3: 0x4343, 0x12f4: 0x2b45, 0x12f5: 0x4347, - 0x12f6: 0x434b, 0x12f7: 0x434f, 0x12f8: 0x4353, 0x12f9: 0x4357, 0x12fa: 0x435b, 0x12fb: 0x306a, - 0x12fc: 0x435f, 0x12fd: 0x4363, 0x12fe: 0x4367, 0x12ff: 0x436b, - // Block 0x4c, offset 0x1300 - 0x1300: 0x436f, 0x1301: 0x4373, 0x1302: 0x4377, 0x1303: 0x437b, 0x1304: 0x1a66, 0x1305: 0x437f, - 0x1306: 0x4384, 0x1307: 0x4388, 0x1308: 0x438c, 0x1309: 0x4390, 0x130a: 0x4394, 0x130b: 0x4398, - 0x130c: 0x439d, 0x130d: 0x43a2, 0x130e: 0x43a6, 0x130f: 0x43aa, 0x1310: 0x307e, 0x1311: 0x3082, - 0x1312: 0x1a82, 0x1313: 0x43ae, 0x1314: 0x43b3, 0x1315: 0x43b7, 0x1316: 0x43bb, 0x1317: 0x43bf, - 0x1318: 0x43c3, 0x1319: 0x43c8, 0x131a: 0x43cd, 0x131b: 0x43d1, 0x131c: 0x43d5, 0x131d: 0x43d9, - 0x131e: 0x43de, 0x131f: 0x3086, 0x1320: 0x43e2, 0x1321: 0x43e7, 0x1322: 0x43ec, 0x1323: 0x43f0, - 0x1324: 0x43f4, 0x1325: 0x43f8, 0x1326: 0x43fd, 0x1327: 0x4401, 0x1328: 0x4405, 0x1329: 0x4409, - 0x132a: 0x440d, 0x132b: 0x4411, 0x132c: 0x4415, 0x132d: 0x4419, 0x132e: 0x441e, 0x132f: 0x4422, - 0x1330: 0x4426, 0x1331: 0x442a, 0x1332: 0x442f, 0x1333: 0x4433, 0x1334: 0x4437, 0x1335: 0x443b, - 0x1336: 0x443f, 0x1337: 0x4444, 0x1338: 0x4449, 0x1339: 0x444d, 0x133a: 0x4451, 0x133b: 0x4455, - 0x133c: 0x445a, 0x133d: 0x445e, 0x133e: 0x309e, 0x133f: 0x309e, - // Block 0x4d, offset 0x1340 - 0x1340: 0x4463, 0x1341: 0x4467, 0x1342: 0x446c, 0x1343: 0x4470, 0x1344: 0x4474, 0x1345: 0x4478, - 0x1346: 0x447c, 0x1347: 0x4480, 0x1348: 0x4484, 0x1349: 0x4488, 0x134a: 0x30a2, 0x134b: 0x448d, - 0x134c: 0x4491, 0x134d: 0x4495, 0x134e: 0x4499, 0x134f: 0x449d, 0x1350: 0x44a1, 0x1351: 0x44a6, - 0x1352: 0x44aa, 0x1353: 0x44af, 0x1354: 0x44b4, 0x1355: 0x1b42, 0x1356: 0x44b9, 0x1357: 0x1b52, - 0x1358: 0x44bd, 0x1359: 0x44c1, 0x135a: 0x44c5, 0x135b: 0x44c9, 0x135c: 0x1b66, 0x135d: 0x44cd, + 0x0540: 0x436f, 0x0541: 0x4373, 0x0542: 0x4377, 0x0543: 0x437b, 0x0544: 0x1a66, 0x0545: 0x437f, + 0x0546: 0x4384, 0x0547: 0x4388, 0x0548: 0x438c, 0x0549: 0x4390, 0x054a: 0x4394, 0x054b: 0x4398, + 0x054c: 0x439d, 0x054d: 0x43a2, 0x054e: 0x43a6, 0x054f: 0x43aa, 0x0550: 0x307e, 0x0551: 0x3082, + 0x0552: 0x1a82, 0x0553: 0x43ae, 0x0554: 0x43b3, 0x0555: 0x43b7, 0x0556: 0x43bb, 0x0557: 0x43bf, + 0x0558: 0x43c3, 0x0559: 0x43c8, 0x055a: 0x43cd, 0x055b: 0x43d1, 0x055c: 0x43d5, 0x055d: 0x43d9, + 0x055e: 0x43de, 0x055f: 0x3086, 0x0560: 0x43e2, 0x0561: 0x43e7, 0x0562: 0x43ec, 0x0563: 0x43f0, + 0x0564: 0x43f4, 0x0565: 0x43f8, 0x0566: 0x43fd, 0x0567: 0x4401, 0x0568: 0x4405, 0x0569: 0x4409, + 0x056a: 0x440d, 0x056b: 0x4411, 0x056c: 0x4415, 0x056d: 0x4419, 0x056e: 0x441e, 0x056f: 0x4422, + 0x0570: 0x4426, 0x0571: 0x442a, 0x0572: 0x442f, 0x0573: 0x4433, 0x0574: 0x4437, 0x0575: 0x443b, + 0x0576: 0x443f, 0x0577: 0x4444, 0x0578: 0x4449, 0x0579: 0x444d, 0x057a: 0x4451, 0x057b: 0x4455, + 0x057c: 0x445a, 0x057d: 0x445e, 0x057e: 0x309e, 0x057f: 0x309e, +} + +// nfcDecompSparseOffset: 56 entries, 112 bytes +var nfcDecompSparseOffset = []uint16{0x0, 0xa, 0x10, 0x15, 0x18, 0x22, 0x27, 0x2e, 0x31, 0x38, 0x3e, 0x46, 0x48, 0x4c, 0x50, 0x52, 0x56, 0x59, 0x5c, 0x60, 0x62, 0x64, 0x66, 0x6a, 0x6c, 0x70, 0x7a, 0x82, 0x84, 0x8d, 0x90, 0x9e, 0xa0, 0xa4, 0xa7, 0xa9, 0xaf, 0xbb, 0xc0, 0xc3, 0xc5, 0xc7, 0xd4, 0xe2, 0xed, 0xf4, 0xff, 0x10b, 0x11c, 0x12d, 0x135, 0x139, 0x13d, 0x141, 0x145, 0x147} + +// nfcDecompSparseValues: 341 entries, 1364 bytes +var nfcDecompSparseValues = [341]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0004, lo: 0x09}, + {value: 0x0032, lo: 0x80, hi: 0x85}, + {value: 0x004a, lo: 0x87, hi: 0x8f}, + {value: 0x006e, lo: 0x91, hi: 0x96}, + {value: 0x0086, lo: 0x99, hi: 0x9d}, + {value: 0x009a, lo: 0xa0, hi: 0xa5}, + {value: 0x00b2, lo: 0xa7, hi: 0xaf}, + {value: 0x00d6, lo: 0xb1, hi: 0xb6}, + {value: 0x00ee, lo: 0xb9, hi: 0xbd}, + {value: 0x0102, lo: 0xbf, hi: 0xbf}, + // Block 0x1, offset 0x2 + {value: 0x0004, lo: 0x05}, + {value: 0x0106, lo: 0x80, hi: 0x8f}, + {value: 0x0146, lo: 0x92, hi: 0xa5}, + {value: 0x0196, lo: 0xa8, hi: 0xb0}, + {value: 0x01c0, lo: 0xb4, hi: 0xb7}, + {value: 0x01d0, lo: 0xb9, hi: 0xbe}, + // Block 0x2, offset 0x3 + {value: 0x0004, lo: 0x04}, + {value: 0x01f0, lo: 0x83, hi: 0x88}, + {value: 0x020c, lo: 0x8c, hi: 0x91}, + {value: 0x0224, lo: 0x94, hi: 0xa5}, + {value: 0x026c, lo: 0xa8, hi: 0xbe}, + // Block 0x3, offset 0x4 + {value: 0x0004, lo: 0x02}, + {value: 0x02ca, lo: 0xa0, hi: 0xa1}, + {value: 0x02d2, lo: 0xaf, hi: 0xb0}, + // Block 0x4, offset 0x5 + {value: 0x0004, lo: 0x09}, + {value: 0x03d8, lo: 0x80, hi: 0x9b}, + {value: 0x0448, lo: 0x9e, hi: 0x9f}, + {value: 0x0450, lo: 0xa6, hi: 0xaa}, + {value: 0x0466, lo: 0xab, hi: 0xab}, + {value: 0x046c, lo: 0xac, hi: 0xac}, + {value: 0x0472, lo: 0xad, hi: 0xad}, + {value: 0x0478, lo: 0xae, hi: 0xb0}, + {value: 0x0486, lo: 0xb1, hi: 0xb1}, + {value: 0x048c, lo: 0xb2, hi: 0xb3}, + // Block 0x5, offset 0x6 + {value: 0x0003, lo: 0x04}, + {value: 0x04cc, lo: 0x80, hi: 0x81}, + {value: 0x04d2, lo: 0x83, hi: 0x84}, + {value: 0x04da, lo: 0xb4, hi: 0xb4}, + {value: 0x04e1, lo: 0xbe, hi: 0xbe}, + // Block 0x6, offset 0x7 + {value: 0x0005, lo: 0x06}, + {value: 0x04e3, lo: 0x85, hi: 0x85}, + {value: 0x04ee, lo: 0x86, hi: 0x87}, + {value: 0x04f6, lo: 0x88, hi: 0x8a}, + {value: 0x0505, lo: 0x8c, hi: 0x8c}, + {value: 0x050a, lo: 0x8e, hi: 0x90}, + {value: 0x051b, lo: 0xaa, hi: 0xb0}, + // Block 0x7, offset 0x8 + {value: 0x0005, lo: 0x02}, + {value: 0x0540, lo: 0x8a, hi: 0x8e}, + {value: 0x0562, lo: 0x93, hi: 0x94}, + // Block 0x8, offset 0x9 + {value: 0x0005, lo: 0x06}, + {value: 0x0584, lo: 0x80, hi: 0x81}, + {value: 0x058e, lo: 0x83, hi: 0x83}, + {value: 0x0593, lo: 0x87, hi: 0x87}, + {value: 0x0598, lo: 0x8c, hi: 0x8e}, + {value: 0x05a7, lo: 0x99, hi: 0x99}, + {value: 0x05ac, lo: 0xb9, hi: 0xb9}, + // Block 0x9, offset 0xa + {value: 0x0005, lo: 0x05}, + {value: 0x05b1, lo: 0x90, hi: 0x91}, + {value: 0x05bb, lo: 0x93, hi: 0x93}, + {value: 0x05c0, lo: 0x97, hi: 0x97}, + {value: 0x05c5, lo: 0x9c, hi: 0x9e}, + {value: 0x05d4, lo: 0xb6, hi: 0xb7}, + // Block 0xa, offset 0xb + {value: 0x0005, lo: 0x07}, + {value: 0x05de, lo: 0x81, hi: 0x82}, + {value: 0x05e8, lo: 0x90, hi: 0x93}, + {value: 0x05fc, lo: 0x96, hi: 0x97}, + {value: 0x0606, lo: 0x9a, hi: 0x9f}, + {value: 0x0624, lo: 0xa2, hi: 0xa7}, + {value: 0x0642, lo: 0xaa, hi: 0xb5}, + {value: 0x067e, lo: 0xb8, hi: 0xb9}, + // Block 0xb, offset 0xc + {value: 0x0005, lo: 0x01}, + {value: 0x068d, lo: 0xa2, hi: 0xa6}, + // Block 0xc, offset 0xd + {value: 0x0005, lo: 0x03}, + {value: 0x06ba, lo: 0x80, hi: 0x80}, + {value: 0x06bf, lo: 0x82, hi: 0x82}, + {value: 0x06c4, lo: 0x93, hi: 0x93}, + // Block 0xd, offset 0xe + {value: 0x0007, lo: 0x03}, + {value: 0x06c9, lo: 0xa9, hi: 0xa9}, + {value: 0x06d0, lo: 0xb1, hi: 0xb1}, + {value: 0x06d7, lo: 0xb4, hi: 0xb4}, + // Block 0xe, offset 0xf + {value: 0x0007, lo: 0x01}, + {value: 0x06de, lo: 0x98, hi: 0x9f}, + // Block 0xf, offset 0x10 + {value: 0x0007, lo: 0x03}, + {value: 0x0716, lo: 0x8b, hi: 0x8c}, + {value: 0x0724, lo: 0x9c, hi: 0x9d}, + {value: 0x0732, lo: 0x9f, hi: 0x9f}, + // Block 0x10, offset 0x11 + {value: 0x0007, lo: 0x02}, + {value: 0x0739, lo: 0xb3, hi: 0xb3}, + {value: 0x0740, lo: 0xb6, hi: 0xb6}, + // Block 0x11, offset 0x12 + {value: 0x0007, lo: 0x02}, + {value: 0x0747, lo: 0x99, hi: 0x9b}, + {value: 0x075c, lo: 0x9e, hi: 0x9e}, + // Block 0x12, offset 0x13 + {value: 0x0007, lo: 0x03}, + {value: 0x0763, lo: 0x88, hi: 0x88}, + {value: 0x076a, lo: 0x8b, hi: 0x8c}, + {value: 0x0778, lo: 0x9c, hi: 0x9d}, + // Block 0x13, offset 0x14 + {value: 0x0000, lo: 0x01}, + {value: 0x0786, lo: 0x94, hi: 0x94}, + // Block 0x14, offset 0x15 + {value: 0x0007, lo: 0x01}, + {value: 0x078d, lo: 0x8a, hi: 0x8c}, + // Block 0x15, offset 0x16 + {value: 0x0000, lo: 0x01}, + {value: 0x07a2, lo: 0x88, hi: 0x88}, + // Block 0x16, offset 0x17 + {value: 0x0007, lo: 0x03}, + {value: 0x07a9, lo: 0x80, hi: 0x80}, + {value: 0x07b0, lo: 0x87, hi: 0x88}, + {value: 0x07be, lo: 0x8a, hi: 0x8b}, + // Block 0x17, offset 0x18 + {value: 0x0007, lo: 0x01}, + {value: 0x07cf, lo: 0x8a, hi: 0x8c}, + // Block 0x18, offset 0x19 + {value: 0x0007, lo: 0x03}, + {value: 0x07e4, lo: 0x9a, hi: 0x9a}, + {value: 0x07eb, lo: 0x9c, hi: 0x9d}, + {value: 0x07fc, lo: 0x9e, hi: 0x9e}, + // Block 0x19, offset 0x1a + {value: 0x0007, lo: 0x09}, + {value: 0x0823, lo: 0x83, hi: 0x83}, + {value: 0x082a, lo: 0x8d, hi: 0x8d}, + {value: 0x0831, lo: 0x92, hi: 0x92}, + {value: 0x0838, lo: 0x97, hi: 0x97}, + {value: 0x083f, lo: 0x9c, hi: 0x9c}, + {value: 0x0846, lo: 0xa9, hi: 0xa9}, + {value: 0x084d, lo: 0xb3, hi: 0xb3}, + {value: 0x0854, lo: 0xb5, hi: 0xb6}, + {value: 0x086c, lo: 0xb8, hi: 0xb8}, + // Block 0x1a, offset 0x1b + {value: 0x0007, lo: 0x07}, + {value: 0x087d, lo: 0x81, hi: 0x81}, + {value: 0x0884, lo: 0x93, hi: 0x93}, + {value: 0x088b, lo: 0x9d, hi: 0x9d}, + {value: 0x0892, lo: 0xa2, hi: 0xa2}, + {value: 0x0899, lo: 0xa7, hi: 0xa7}, + {value: 0x08a0, lo: 0xac, hi: 0xac}, + {value: 0x08a7, lo: 0xb9, hi: 0xb9}, + // Block 0x1b, offset 0x1c + {value: 0x0000, lo: 0x01}, + {value: 0x08ae, lo: 0xa6, hi: 0xa6}, + // Block 0x1c, offset 0x1d + {value: 0x0007, lo: 0x08}, + {value: 0x08b9, lo: 0x86, hi: 0x86}, + {value: 0x08c0, lo: 0x88, hi: 0x88}, + {value: 0x08c7, lo: 0x8a, hi: 0x8a}, + {value: 0x08ce, lo: 0x8c, hi: 0x8c}, + {value: 0x08d5, lo: 0x8e, hi: 0x8e}, + {value: 0x08dc, lo: 0x92, hi: 0x92}, + {value: 0x08e3, lo: 0xbb, hi: 0xbb}, + {value: 0x08ea, lo: 0xbd, hi: 0xbd}, + // Block 0x1d, offset 0x1e + {value: 0x0007, lo: 0x02}, + {value: 0x08f1, lo: 0x80, hi: 0x81}, + {value: 0x08ff, lo: 0x83, hi: 0x83}, + // Block 0x1e, offset 0x1f + {value: 0x0004, lo: 0x0d}, + {value: 0x09ea, lo: 0x80, hi: 0x88}, + {value: 0x0a10, lo: 0x89, hi: 0x89}, + {value: 0x0a16, lo: 0x8a, hi: 0x94}, + {value: 0x0a44, lo: 0x95, hi: 0x95}, + {value: 0x0a4a, lo: 0x96, hi: 0x96}, + {value: 0x0a50, lo: 0x97, hi: 0x97}, + {value: 0x0a56, lo: 0x98, hi: 0x9c}, + {value: 0x0a6c, lo: 0x9d, hi: 0x9d}, + {value: 0x0a72, lo: 0x9e, hi: 0xae}, + {value: 0x0ab8, lo: 0xaf, hi: 0xaf}, + {value: 0x0abe, lo: 0xb0, hi: 0xb8}, + {value: 0x0ae4, lo: 0xb9, hi: 0xb9}, + {value: 0x0aea, lo: 0xba, hi: 0xbf}, + // Block 0x1f, offset 0x20 + {value: 0x0004, lo: 0x01}, + {value: 0x142e, lo: 0x80, hi: 0x81}, + // Block 0x20, offset 0x21 + {value: 0x0000, lo: 0x03}, + {value: 0x14d6, lo: 0xa6, hi: 0xa6}, + {value: 0x091c, lo: 0xaa, hi: 0xaa}, + {value: 0x0046, lo: 0xab, hi: 0xab}, + // Block 0x21, offset 0x22 + {value: 0x0006, lo: 0x02}, + {value: 0x159f, lo: 0x9a, hi: 0x9b}, + {value: 0x15ab, lo: 0xae, hi: 0xae}, + // Block 0x22, offset 0x23 + {value: 0x0006, lo: 0x01}, + {value: 0x15b1, lo: 0x8d, hi: 0x8f}, + // Block 0x23, offset 0x24 + {value: 0x0006, lo: 0x05}, + {value: 0x15c3, lo: 0x84, hi: 0x84}, + {value: 0x15c9, lo: 0x89, hi: 0x89}, + {value: 0x15cf, lo: 0x8c, hi: 0x8c}, + {value: 0x15d5, lo: 0xa4, hi: 0xa4}, + {value: 0x15db, lo: 0xa6, hi: 0xa6}, + // Block 0x24, offset 0x25 + {value: 0x0006, lo: 0x0b}, + {value: 0x1603, lo: 0x81, hi: 0x81}, + {value: 0x1609, lo: 0x84, hi: 0x84}, + {value: 0x160f, lo: 0x87, hi: 0x87}, + {value: 0x1615, lo: 0x89, hi: 0x89}, + {value: 0x161b, lo: 0xa0, hi: 0xa0}, + {value: 0x161f, lo: 0xa2, hi: 0xa2}, + {value: 0x1625, lo: 0xad, hi: 0xae}, + {value: 0x162f, lo: 0xaf, hi: 0xaf}, + {value: 0x1633, lo: 0xb0, hi: 0xb1}, + {value: 0x163f, lo: 0xb4, hi: 0xb5}, + {value: 0x164b, lo: 0xb8, hi: 0xb9}, + // Block 0x25, offset 0x26 + {value: 0x0006, lo: 0x04}, + {value: 0x1657, lo: 0x80, hi: 0x81}, + {value: 0x1663, lo: 0x84, hi: 0x85}, + {value: 0x166f, lo: 0x88, hi: 0x89}, + {value: 0x167b, lo: 0xac, hi: 0xaf}, + // Block 0x26, offset 0x27 + {value: 0x0006, lo: 0x02}, + {value: 0x1693, lo: 0xa0, hi: 0xa3}, + {value: 0x16ab, lo: 0xaa, hi: 0xad}, + // Block 0x27, offset 0x28 + {value: 0x0004, lo: 0x01}, + {value: 0x16c3, lo: 0xa9, hi: 0xaa}, + // Block 0x28, offset 0x29 + {value: 0x0000, lo: 0x01}, + {value: 0x1814, lo: 0x9c, hi: 0x9c}, + // Block 0x29, offset 0x2a + {value: 0x0007, lo: 0x0c}, + {value: 0x1c39, lo: 0x94, hi: 0x94}, + {value: 0x1c4a, lo: 0x9e, hi: 0x9e}, + {value: 0x1c58, lo: 0xac, hi: 0xac}, + {value: 0x1c5f, lo: 0xae, hi: 0xae}, + {value: 0x1c66, lo: 0xb0, hi: 0xb0}, + {value: 0x1c6d, lo: 0xb2, hi: 0xb2}, + {value: 0x1c74, lo: 0xb4, hi: 0xb4}, + {value: 0x1c7b, lo: 0xb6, hi: 0xb6}, + {value: 0x1c82, lo: 0xb8, hi: 0xb8}, + {value: 0x1c89, lo: 0xba, hi: 0xba}, + {value: 0x1c90, lo: 0xbc, hi: 0xbc}, + {value: 0x1c97, lo: 0xbe, hi: 0xbe}, + // Block 0x2a, offset 0x2b + {value: 0x0007, lo: 0x0d}, + {value: 0x1c9e, lo: 0x80, hi: 0x80}, + {value: 0x1ca5, lo: 0x82, hi: 0x82}, + {value: 0x1cac, lo: 0x85, hi: 0x85}, + {value: 0x1cb3, lo: 0x87, hi: 0x87}, + {value: 0x1cba, lo: 0x89, hi: 0x89}, + {value: 0x1cc1, lo: 0x90, hi: 0x91}, + {value: 0x1ccf, lo: 0x93, hi: 0x94}, + {value: 0x1cdd, lo: 0x96, hi: 0x97}, + {value: 0x1ceb, lo: 0x99, hi: 0x9a}, + {value: 0x1cf9, lo: 0x9c, hi: 0x9d}, + {value: 0x1d07, lo: 0xb4, hi: 0xb4}, + {value: 0x1d0e, lo: 0xb7, hi: 0xba}, + {value: 0x1d2a, lo: 0xbe, hi: 0xbe}, + // Block 0x2b, offset 0x2c + {value: 0x0004, lo: 0x0a}, + {value: 0x2a81, lo: 0x80, hi: 0x81}, + {value: 0x1a9e, lo: 0x82, hi: 0x82}, + {value: 0x2a89, lo: 0x83, hi: 0x86}, + {value: 0x1b76, lo: 0x87, hi: 0x87}, + {value: 0x1b76, lo: 0x88, hi: 0x88}, + {value: 0x2a99, lo: 0x89, hi: 0x89}, + {value: 0x1abe, lo: 0x8a, hi: 0x8a}, + {value: 0x2a9d, lo: 0x8b, hi: 0xb3}, + {value: 0x1a16, lo: 0xb4, hi: 0xb4}, + {value: 0x2b41, lo: 0xb5, hi: 0xbf}, + // Block 0x2c, offset 0x2d + {value: 0x0004, lo: 0x06}, + {value: 0x1b3a, lo: 0x80, hi: 0x80}, + {value: 0x2b6d, lo: 0x81, hi: 0x9b}, + {value: 0x2ac1, lo: 0x9c, hi: 0x9c}, + {value: 0x2bd9, lo: 0x9d, hi: 0xb0}, + {value: 0x1aa6, lo: 0xb1, hi: 0xb1}, + {value: 0x2c29, lo: 0xb2, hi: 0xbf}, + // Block 0x2d, offset 0x2e + {value: 0x0004, lo: 0x0a}, + {value: 0x2c61, lo: 0x80, hi: 0x80}, + {value: 0x18ba, lo: 0x81, hi: 0x81}, + {value: 0x2c65, lo: 0x82, hi: 0x89}, + {value: 0x186e, lo: 0x8a, hi: 0x8a}, + {value: 0x2c85, lo: 0x8b, hi: 0xa0}, + {value: 0x2c21, lo: 0xa1, hi: 0xa1}, + {value: 0x2cdd, lo: 0xa2, hi: 0xa9}, + {value: 0x2be1, lo: 0xaa, hi: 0xaa}, + {value: 0x2cfd, lo: 0xab, hi: 0xbe}, + {value: 0x2ac1, lo: 0xbf, hi: 0xbf}, + // Block 0x2e, offset 0x2f + {value: 0x0004, lo: 0x0b}, + {value: 0x2d4d, lo: 0x80, hi: 0x83}, + {value: 0x1b72, lo: 0x84, hi: 0x84}, + {value: 0x2d5d, lo: 0x85, hi: 0x90}, + {value: 0x2173, lo: 0x91, hi: 0x91}, + {value: 0x2d8d, lo: 0x92, hi: 0x9a}, + {value: 0x2be9, lo: 0x9b, hi: 0x9b}, + {value: 0x2db1, lo: 0x9c, hi: 0xa8}, + {value: 0x1aba, lo: 0xa9, hi: 0xa9}, + {value: 0x2de5, lo: 0xaa, hi: 0xb6}, + {value: 0x19f6, lo: 0xb7, hi: 0xb7}, + {value: 0x2e19, lo: 0xb8, hi: 0xbf}, + // Block 0x2f, offset 0x30 + {value: 0x0004, lo: 0x10}, + {value: 0x2e39, lo: 0x80, hi: 0x87}, + {value: 0x1a62, lo: 0x88, hi: 0x88}, + {value: 0x2e59, lo: 0x89, hi: 0x89}, + {value: 0x1a6e, lo: 0x8a, hi: 0x8a}, + {value: 0x2e5d, lo: 0x8b, hi: 0x8d}, + {value: 0x2e69, lo: 0x90, hi: 0x90}, + {value: 0x2e6d, lo: 0x92, hi: 0x92}, + {value: 0x2e71, lo: 0x95, hi: 0x9d}, + {value: 0x1a12, lo: 0x9e, hi: 0x9e}, + {value: 0x2e95, lo: 0xa0, hi: 0xa0}, + {value: 0x2e99, lo: 0xa2, hi: 0xa2}, + {value: 0x2e9d, lo: 0xa5, hi: 0xa6}, + {value: 0x2ea5, lo: 0xaa, hi: 0xad}, + {value: 0x2eb5, lo: 0xb0, hi: 0xbb}, + {value: 0x18d6, lo: 0xbc, hi: 0xbc}, + {value: 0x2ee5, lo: 0xbd, hi: 0xbf}, + // Block 0x30, offset 0x31 + {value: 0x0004, lo: 0x10}, + {value: 0x2ef1, lo: 0x80, hi: 0x8b}, + {value: 0x2187, lo: 0x8c, hi: 0x8c}, + {value: 0x2f21, lo: 0x8d, hi: 0x90}, + {value: 0x2197, lo: 0x91, hi: 0x91}, + {value: 0x2f31, lo: 0x92, hi: 0x96}, + {value: 0x2cb1, lo: 0x97, hi: 0x97}, + {value: 0x2f45, lo: 0x98, hi: 0x9d}, + {value: 0x2f59, lo: 0x9e, hi: 0xa6}, + {value: 0x2e9d, lo: 0xa7, hi: 0xa7}, + {value: 0x2f7d, lo: 0xa8, hi: 0xac}, + {value: 0x2f92, lo: 0xad, hi: 0xad}, + {value: 0x2f96, lo: 0xb0, hi: 0xb7}, + {value: 0x2ecd, lo: 0xb8, hi: 0xb8}, + {value: 0x2fb6, lo: 0xb9, hi: 0xbb}, + {value: 0x2e69, lo: 0xbc, hi: 0xbc}, + {value: 0x2fc2, lo: 0xbd, hi: 0xbf}, + // Block 0x31, offset 0x32 + {value: 0x0005, lo: 0x07}, + {value: 0x3105, lo: 0x9d, hi: 0x9d}, + {value: 0x310a, lo: 0x9f, hi: 0x9f}, + {value: 0x3124, lo: 0xaa, hi: 0xac}, + {value: 0x3135, lo: 0xad, hi: 0xad}, + {value: 0x313c, lo: 0xae, hi: 0xb6}, + {value: 0x3169, lo: 0xb8, hi: 0xbc}, + {value: 0x3182, lo: 0xbe, hi: 0xbe}, + // Block 0x32, offset 0x33 + {value: 0x0005, lo: 0x03}, + {value: 0x3187, lo: 0x80, hi: 0x81}, + {value: 0x3191, lo: 0x83, hi: 0x84}, + {value: 0x319b, lo: 0x86, hi: 0x8e}, + // Block 0x33, offset 0x34 + {value: 0x0009, lo: 0x03}, + {value: 0x3a73, lo: 0x9a, hi: 0x9a}, + {value: 0x3a7c, lo: 0x9c, hi: 0x9c}, + {value: 0x3a85, lo: 0xab, hi: 0xab}, + // Block 0x34, offset 0x35 + {value: 0x000d, lo: 0x03}, + {value: 0x3a8e, lo: 0x9e, hi: 0x9e}, + {value: 0x3a97, lo: 0x9f, hi: 0x9f}, + {value: 0x3aa0, lo: 0xa0, hi: 0xa4}, + // Block 0x35, offset 0x36 + {value: 0x0009, lo: 0x03}, + {value: 0x3ae1, lo: 0xbb, hi: 0xbd}, + {value: 0x3b00, lo: 0xbe, hi: 0xbe}, + {value: 0x3b0d, lo: 0xbf, hi: 0xbf}, + // Block 0x36, offset 0x37 + {value: 0x0000, lo: 0x01}, + {value: 0x3b1a, lo: 0x80, hi: 0x80}, + // Block 0x37, offset 0x38 + {value: 0x0004, lo: 0x0d}, + {value: 0x4463, lo: 0x80, hi: 0x81}, + {value: 0x446c, lo: 0x82, hi: 0x89}, + {value: 0x30a2, lo: 0x8a, hi: 0x8a}, + {value: 0x448d, lo: 0x8b, hi: 0x90}, + {value: 0x44a6, lo: 0x91, hi: 0x92}, + {value: 0x44af, lo: 0x93, hi: 0x93}, + {value: 0x44b4, lo: 0x94, hi: 0x94}, + {value: 0x1b42, lo: 0x95, hi: 0x95}, + {value: 0x44b9, lo: 0x96, hi: 0x96}, + {value: 0x1b52, lo: 0x97, hi: 0x97}, + {value: 0x44bd, lo: 0x98, hi: 0x9b}, + {value: 0x1b66, lo: 0x9c, hi: 0x9c}, + {value: 0x44cd, lo: 0x9d, hi: 0x9d}, } // nfcDecompLookup: 832 bytes @@ -3038,37 +3125,37 @@ var nfcDecompLookup = [832]uint8{ // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x0c3: 0x03, 0x0c4: 0x04, 0x0c5: 0x05, 0x0c6: 0x06, 0x0c7: 0x07, - 0x0c8: 0x08, 0x0cd: 0x09, 0x0ce: 0x0a, 0x0cf: 0x0b, - 0x0d0: 0x0c, 0x0d1: 0x0d, 0x0d3: 0x0e, - 0x0d8: 0x0f, 0x0db: 0x10, + 0x0c3: 0x16, 0x0c4: 0x17, 0x0c5: 0x18, 0x0c6: 0x19, 0x0c7: 0x03, + 0x0c8: 0x1a, 0x0cd: 0x1b, 0x0ce: 0x1c, 0x0cf: 0x1d, + 0x0d0: 0x1e, 0x0d1: 0x1f, 0x0d3: 0x20, + 0x0d8: 0x21, 0x0db: 0x22, 0x0e0: 0x04, 0x0e1: 0x05, 0x0e2: 0x06, 0x0e3: 0x07, 0x0ef: 0x08, 0x0f0: 0x0c, // Block 0x4, offset 0x100 - 0x124: 0x11, 0x125: 0x12, 0x127: 0x13, - 0x128: 0x14, 0x129: 0x15, 0x12d: 0x16, 0x12e: 0x17, 0x12f: 0x18, - 0x131: 0x19, 0x133: 0x1a, 0x135: 0x1b, 0x137: 0x1c, - 0x13d: 0x1d, 0x13e: 0x1e, + 0x124: 0x23, 0x125: 0x24, 0x127: 0x25, + 0x128: 0x26, 0x129: 0x27, 0x12d: 0x28, 0x12e: 0x29, 0x12f: 0x2a, + 0x131: 0x2b, 0x133: 0x2c, 0x135: 0x2d, 0x137: 0x2e, + 0x13d: 0x2f, 0x13e: 0x30, // Block 0x5, offset 0x140 - 0x140: 0x1f, - 0x16c: 0x20, 0x16d: 0x21, - 0x178: 0x22, 0x179: 0x23, 0x17a: 0x24, 0x17b: 0x25, 0x17c: 0x26, 0x17d: 0x27, 0x17e: 0x28, 0x17f: 0x29, + 0x140: 0x31, + 0x16c: 0x32, 0x16d: 0x33, + 0x178: 0x34, 0x179: 0x04, 0x17a: 0x05, 0x17b: 0x06, 0x17c: 0x07, 0x17d: 0x08, 0x17e: 0x09, 0x17f: 0x0a, // Block 0x6, offset 0x180 - 0x180: 0x2a, 0x184: 0x2b, 0x186: 0x2c, 0x187: 0x2d, - 0x188: 0x2e, 0x189: 0x2f, 0x18a: 0x30, 0x18b: 0x31, 0x18c: 0x32, - 0x1ab: 0x33, + 0x180: 0x35, 0x184: 0x36, 0x186: 0x37, 0x187: 0x38, + 0x188: 0x39, 0x189: 0x3a, 0x18a: 0x3b, 0x18b: 0x3c, 0x18c: 0x3d, + 0x1ab: 0x3e, // Block 0x7, offset 0x1c0 - 0x1c1: 0x34, 0x1c2: 0x35, 0x1c3: 0x36, + 0x1c1: 0x0b, 0x1c2: 0x3f, 0x1c3: 0x40, // Block 0x8, offset 0x200 - 0x224: 0x37, 0x225: 0x38, 0x226: 0x39, 0x227: 0x3a, - 0x228: 0x3b, 0x229: 0x3c, 0x22a: 0x3d, 0x22b: 0x3e, 0x22c: 0x3f, 0x22d: 0x40, + 0x224: 0x41, 0x225: 0x42, 0x226: 0x43, 0x227: 0x44, + 0x228: 0x45, 0x229: 0x46, 0x22a: 0x0c, 0x22b: 0x0d, 0x22c: 0x47, 0x22d: 0x48, // Block 0x9, offset 0x240 - 0x242: 0x41, + 0x242: 0x49, // Block 0xa, offset 0x280 - 0x285: 0x42, 0x286: 0x43, 0x287: 0x44, + 0x285: 0x4a, 0x286: 0x4b, 0x287: 0x4c, // Block 0xb, offset 0x2c0 - 0x2e0: 0x45, 0x2e1: 0x46, 0x2e2: 0x47, 0x2e3: 0x48, 0x2e4: 0x49, 0x2e5: 0x4a, 0x2e6: 0x4b, 0x2e7: 0x4c, + 0x2e0: 0x0e, 0x2e1: 0x0f, 0x2e2: 0x10, 0x2e3: 0x11, 0x2e4: 0x12, 0x2e5: 0x13, 0x2e6: 0x14, 0x2e7: 0x15, 0x2e8: 0x4d, // Block 0xc, offset 0x300 0x311: 0x09, @@ -3076,1288 +3163,1442 @@ var nfcDecompLookup = [832]uint8{ 0x32f: 0x0b, } -var nfcDecompTrie = trie{nfcDecompLookup[:], nfcDecompValues[:]} +var nfcDecompTrie = trie{nfcDecompLookup[:], nfcDecompValues[:], nfcDecompSparseValues[:], nfcDecompSparseOffset[:], 22} -// nfkcDecompValues: 10176 entries, 20352 bytes +// nfkcDecompValues: 4224 entries, 8448 bytes // Block 2 is the null block. -var nfkcDecompValues = [10176]uint16{ +var nfkcDecompValues = [4224]uint16{ // Block 0x0, offset 0x0 // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x00e0: 0x0001, - 0x00e8: 0x0003, - 0x00ea: 0x0007, 0x00ef: 0x0009, - 0x00f2: 0x000d, 0x00f3: 0x000f, 0x00f4: 0x0011, 0x00f5: 0x0015, - 0x00f8: 0x0018, 0x00f9: 0x001c, 0x00fa: 0x001e, - 0x00fc: 0x0020, 0x00fd: 0x0026, 0x00fe: 0x002c, + 0x00c4: 0x02da, 0x00c5: 0x02df, + 0x00c6: 0x02e4, 0x00c7: 0x02e9, 0x00c8: 0x02ec, 0x00c9: 0x02ef, 0x00ca: 0x02f2, 0x00cb: 0x02f5, + 0x00cc: 0x02f8, 0x00cd: 0x02fb, 0x00ce: 0x02ff, 0x00cf: 0x0303, 0x00d0: 0x0307, 0x00d1: 0x030b, + 0x00d2: 0x030f, 0x00d3: 0x0313, 0x00d4: 0x0317, 0x00d5: 0x031b, 0x00d6: 0x0321, 0x00d7: 0x0327, + 0x00d8: 0x032d, 0x00d9: 0x0333, 0x00da: 0x0339, 0x00db: 0x033f, 0x00dc: 0x0345, + 0x00de: 0x034b, 0x00df: 0x0351, 0x00e0: 0x0357, 0x00e1: 0x035d, 0x00e2: 0x0363, 0x00e3: 0x0368, + 0x00e6: 0x036d, 0x00e7: 0x0371, 0x00e8: 0x0375, 0x00e9: 0x0379, + 0x00ea: 0x037d, 0x00eb: 0x0381, 0x00ec: 0x0385, 0x00ed: 0x038b, 0x00ee: 0x0391, 0x00ef: 0x0396, + 0x00f0: 0x039b, 0x00f1: 0x039f, 0x00f2: 0x03a2, 0x00f3: 0x03a5, 0x00f4: 0x03a8, 0x00f5: 0x03ac, + 0x00f8: 0x03b0, 0x00f9: 0x03b4, 0x00fa: 0x03b8, 0x00fb: 0x03be, + 0x00fc: 0x03c4, 0x00fd: 0x03c9, 0x00fe: 0x03ce, 0x00ff: 0x03d3, // Block 0x4, offset 0x100 - 0x0100: 0x0032, 0x0101: 0x0036, 0x0102: 0x003a, 0x0103: 0x003e, 0x0104: 0x0042, 0x0105: 0x0046, - 0x0107: 0x004a, 0x0108: 0x004e, 0x0109: 0x0052, 0x010a: 0x0056, 0x010b: 0x005a, - 0x010c: 0x005e, 0x010d: 0x0062, 0x010e: 0x0066, 0x010f: 0x006a, 0x0111: 0x006e, - 0x0112: 0x0072, 0x0113: 0x0076, 0x0114: 0x007a, 0x0115: 0x007e, 0x0116: 0x0082, - 0x0119: 0x0086, 0x011a: 0x008a, 0x011b: 0x008e, 0x011c: 0x0092, 0x011d: 0x0096, - 0x0120: 0x009a, 0x0121: 0x009e, 0x0122: 0x00a2, 0x0123: 0x00a6, - 0x0124: 0x00aa, 0x0125: 0x00ae, 0x0127: 0x00b2, 0x0128: 0x00b6, 0x0129: 0x00ba, - 0x012a: 0x00be, 0x012b: 0x00c2, 0x012c: 0x00c6, 0x012d: 0x00ca, 0x012e: 0x00ce, 0x012f: 0x00d2, - 0x0131: 0x00d6, 0x0132: 0x00da, 0x0133: 0x00de, 0x0134: 0x00e2, 0x0135: 0x00e6, - 0x0136: 0x00ea, 0x0139: 0x00ee, 0x013a: 0x00f2, 0x013b: 0x00f6, - 0x013c: 0x00fa, 0x013d: 0x00fe, 0x013f: 0x0102, + 0x0100: 0x092d, 0x0101: 0x092f, 0x0102: 0x0931, 0x0103: 0x0007, 0x0104: 0x0933, 0x0105: 0x0936, + 0x0106: 0x0939, 0x0107: 0x093d, 0x0108: 0x093f, 0x0109: 0x0941, 0x010a: 0x0943, 0x010b: 0x0946, + 0x010c: 0x0949, 0x010d: 0x094c, 0x010f: 0x094e, 0x0110: 0x0950, 0x0111: 0x0952, + 0x0112: 0x001e, 0x0113: 0x0955, 0x0114: 0x0958, 0x0115: 0x095c, 0x0116: 0x0960, 0x0117: 0x0962, + 0x0118: 0x0964, 0x0119: 0x0966, 0x011a: 0x096a, 0x011b: 0x096d, 0x011c: 0x096f, 0x011d: 0x0559, + 0x011e: 0x0973, 0x011f: 0x0976, 0x0120: 0x056c, 0x0121: 0x0979, 0x0122: 0x097c, 0x0123: 0x049b, + 0x0124: 0x0964, 0x0125: 0x096d, 0x0126: 0x0559, 0x0127: 0x0973, 0x0128: 0x0575, 0x0129: 0x056c, + 0x012a: 0x0979, + 0x0138: 0x097e, // Block 0x5, offset 0x140 - 0x0140: 0x0106, 0x0141: 0x010a, 0x0142: 0x010e, 0x0143: 0x0112, 0x0144: 0x0116, 0x0145: 0x011a, - 0x0146: 0x011e, 0x0147: 0x0122, 0x0148: 0x0126, 0x0149: 0x012a, 0x014a: 0x012e, 0x014b: 0x0132, - 0x014c: 0x0136, 0x014d: 0x013a, 0x014e: 0x013e, 0x014f: 0x0142, - 0x0152: 0x0146, 0x0153: 0x014a, 0x0154: 0x014e, 0x0155: 0x0152, 0x0156: 0x0156, 0x0157: 0x015a, - 0x0158: 0x015e, 0x0159: 0x0162, 0x015a: 0x0166, 0x015b: 0x016a, 0x015c: 0x016e, 0x015d: 0x0172, - 0x015e: 0x0176, 0x015f: 0x017a, 0x0160: 0x017e, 0x0161: 0x0182, 0x0162: 0x0186, 0x0163: 0x018a, - 0x0164: 0x018e, 0x0165: 0x0192, 0x0168: 0x0196, 0x0169: 0x019a, - 0x016a: 0x019e, 0x016b: 0x01a2, 0x016c: 0x01a6, 0x016d: 0x01aa, 0x016e: 0x01ae, 0x016f: 0x01b2, - 0x0170: 0x01b6, 0x0172: 0x01ba, 0x0173: 0x01bd, 0x0174: 0x01c0, 0x0175: 0x01c4, - 0x0176: 0x01c8, 0x0177: 0x01cc, 0x0179: 0x01d0, 0x017a: 0x01d4, 0x017b: 0x01d8, - 0x017c: 0x01dc, 0x017d: 0x01e0, 0x017e: 0x01e4, 0x017f: 0x01e8, + 0x0140: 0x0b02, 0x0141: 0x0b06, 0x0142: 0x0b0a, 0x0143: 0x0b0e, 0x0144: 0x0b12, 0x0145: 0x0b16, + 0x0146: 0x0b1a, 0x0147: 0x0b1e, 0x0148: 0x0b22, 0x0149: 0x0b26, 0x014a: 0x0b2a, 0x014b: 0x0b2e, + 0x014c: 0x0b32, 0x014d: 0x0b38, 0x014e: 0x0b3e, 0x014f: 0x0b44, 0x0150: 0x0b4a, 0x0151: 0x0b50, + 0x0152: 0x0b56, 0x0153: 0x0b5c, 0x0154: 0x0b62, 0x0155: 0x0b66, 0x0156: 0x0b6a, 0x0157: 0x0b6e, + 0x0158: 0x0b72, 0x0159: 0x0b76, 0x015a: 0x0b7a, 0x015b: 0x0b7e, 0x015c: 0x0b82, 0x015d: 0x0b88, + 0x015e: 0x0b8e, 0x015f: 0x0b92, 0x0160: 0x0b96, 0x0161: 0x0b9a, 0x0162: 0x0b9e, 0x0163: 0x0ba2, + 0x0164: 0x0ba6, 0x0165: 0x0bac, 0x0166: 0x0bb2, 0x0167: 0x0bb8, 0x0168: 0x0bbe, 0x0169: 0x0bc4, + 0x016a: 0x0bca, 0x016b: 0x0bce, 0x016c: 0x0bd2, 0x016d: 0x0bd6, 0x016e: 0x0bda, 0x016f: 0x0bde, + 0x0170: 0x0be2, 0x0171: 0x0be6, 0x0172: 0x0bea, 0x0173: 0x0bee, 0x0174: 0x0bf2, 0x0175: 0x0bf6, + 0x0176: 0x0bfa, 0x0177: 0x0bfe, 0x0178: 0x0c02, 0x0179: 0x0c08, 0x017a: 0x0c0e, 0x017b: 0x0c14, + 0x017c: 0x0c1a, 0x017d: 0x0c1e, 0x017e: 0x0c22, 0x017f: 0x0c26, // Block 0x6, offset 0x180 - 0x0180: 0x01ec, 0x0183: 0x01f0, 0x0184: 0x01f4, 0x0185: 0x01f8, - 0x0186: 0x01fc, 0x0187: 0x0200, 0x0188: 0x0204, 0x0189: 0x0208, - 0x018c: 0x020c, 0x018d: 0x0210, 0x018e: 0x0214, 0x018f: 0x0218, 0x0190: 0x021c, 0x0191: 0x0220, - 0x0194: 0x0224, 0x0195: 0x0228, 0x0196: 0x022c, 0x0197: 0x0230, - 0x0198: 0x0234, 0x0199: 0x0238, 0x019a: 0x023c, 0x019b: 0x0240, 0x019c: 0x0244, 0x019d: 0x0248, - 0x019e: 0x024c, 0x019f: 0x0250, 0x01a0: 0x0254, 0x01a1: 0x0258, 0x01a2: 0x025c, 0x01a3: 0x0260, - 0x01a4: 0x0264, 0x01a5: 0x0268, 0x01a8: 0x026c, 0x01a9: 0x0270, - 0x01aa: 0x0274, 0x01ab: 0x0278, 0x01ac: 0x027c, 0x01ad: 0x0280, 0x01ae: 0x0284, 0x01af: 0x0288, - 0x01b0: 0x028c, 0x01b1: 0x0290, 0x01b2: 0x0294, 0x01b3: 0x0298, 0x01b4: 0x029c, 0x01b5: 0x02a0, - 0x01b6: 0x02a4, 0x01b7: 0x02a8, 0x01b8: 0x02ac, 0x01b9: 0x02b0, 0x01ba: 0x02b4, 0x01bb: 0x02b8, - 0x01bc: 0x02bc, 0x01bd: 0x02c0, 0x01be: 0x02c4, 0x01bf: 0x02c8, + 0x0180: 0x0c2a, 0x0181: 0x0c2e, 0x0182: 0x0c32, 0x0183: 0x0c36, 0x0184: 0x0c3a, 0x0185: 0x0c3e, + 0x0186: 0x0c42, 0x0187: 0x0c46, 0x0188: 0x0c4a, 0x0189: 0x0c4e, 0x018a: 0x0c52, 0x018b: 0x0c56, + 0x018c: 0x0c5a, 0x018d: 0x0c5e, 0x018e: 0x0c62, 0x018f: 0x0c66, 0x0190: 0x0c6a, 0x0191: 0x0c6e, + 0x0192: 0x0c72, 0x0193: 0x0c76, 0x0194: 0x0c7a, 0x0195: 0x0c7e, 0x0196: 0x0c82, 0x0197: 0x0c86, + 0x0198: 0x0c8a, 0x0199: 0x0c8e, 0x019a: 0x0c92, 0x019b: 0x0b9a, + 0x01a0: 0x0c9b, 0x01a1: 0x0c9f, 0x01a2: 0x0ca3, 0x01a3: 0x0ca7, + 0x01a4: 0x0cab, 0x01a5: 0x0cb1, 0x01a6: 0x0cb7, 0x01a7: 0x0cbd, 0x01a8: 0x0cc3, 0x01a9: 0x0cc9, + 0x01aa: 0x0ccf, 0x01ab: 0x0cd5, 0x01ac: 0x0cdb, 0x01ad: 0x0ce1, 0x01ae: 0x0ce7, 0x01af: 0x0ced, + 0x01b0: 0x0cf3, 0x01b1: 0x0cf9, 0x01b2: 0x0cff, 0x01b3: 0x0d05, 0x01b4: 0x0d0b, 0x01b5: 0x0d11, + 0x01b6: 0x0d17, 0x01b7: 0x0d1d, 0x01b8: 0x0d23, 0x01b9: 0x0d27, 0x01ba: 0x0d2b, 0x01bb: 0x0d2f, + 0x01bc: 0x0d33, 0x01bd: 0x0d37, 0x01be: 0x0d3b, 0x01bf: 0x0d41, // Block 0x7, offset 0x1c0 - 0x01e0: 0x02ca, 0x01e1: 0x02ce, - 0x01ef: 0x02d2, - 0x01f0: 0x02d6, + 0x01c0: 0x0d47, 0x01c1: 0x0d4d, 0x01c2: 0x0d53, 0x01c3: 0x0d59, 0x01c4: 0x0d5f, 0x01c5: 0x0d65, + 0x01c6: 0x0d6b, 0x01c7: 0x0d71, 0x01c8: 0x0d77, 0x01c9: 0x0d7b, 0x01ca: 0x0d7f, 0x01cb: 0x0d83, + 0x01cc: 0x0d87, 0x01cd: 0x0d8b, 0x01ce: 0x0d8f, 0x01cf: 0x0d93, 0x01d0: 0x0d97, 0x01d1: 0x0d9d, + 0x01d2: 0x0da3, 0x01d3: 0x0da9, 0x01d4: 0x0daf, 0x01d5: 0x0db5, 0x01d6: 0x0dbb, 0x01d7: 0x0dc1, + 0x01d8: 0x0dc7, 0x01d9: 0x0dcd, 0x01da: 0x0dd3, 0x01db: 0x0dd9, 0x01dc: 0x0ddf, 0x01dd: 0x0de5, + 0x01de: 0x0deb, 0x01df: 0x0df1, 0x01e0: 0x0df7, 0x01e1: 0x0dfd, 0x01e2: 0x0e03, 0x01e3: 0x0e09, + 0x01e4: 0x0e0f, 0x01e5: 0x0e13, 0x01e6: 0x0e17, 0x01e7: 0x0e1b, 0x01e8: 0x0e1f, 0x01e9: 0x0e25, + 0x01ea: 0x0e2b, 0x01eb: 0x0e31, 0x01ec: 0x0e37, 0x01ed: 0x0e3d, 0x01ee: 0x0e43, 0x01ef: 0x0e49, + 0x01f0: 0x0e4f, 0x01f1: 0x0e55, 0x01f2: 0x0e5b, 0x01f3: 0x0e5f, 0x01f4: 0x0e63, 0x01f5: 0x0e67, + 0x01f6: 0x0e6b, 0x01f7: 0x0e6f, 0x01f8: 0x0e73, 0x01f9: 0x0e77, // Block 0x8, offset 0x200 - 0x0204: 0x02da, 0x0205: 0x02df, - 0x0206: 0x02e4, 0x0207: 0x02e9, 0x0208: 0x02ec, 0x0209: 0x02ef, 0x020a: 0x02f2, 0x020b: 0x02f5, - 0x020c: 0x02f8, 0x020d: 0x02fb, 0x020e: 0x02ff, 0x020f: 0x0303, 0x0210: 0x0307, 0x0211: 0x030b, - 0x0212: 0x030f, 0x0213: 0x0313, 0x0214: 0x0317, 0x0215: 0x031b, 0x0216: 0x0321, 0x0217: 0x0327, - 0x0218: 0x032d, 0x0219: 0x0333, 0x021a: 0x0339, 0x021b: 0x033f, 0x021c: 0x0345, - 0x021e: 0x034b, 0x021f: 0x0351, 0x0220: 0x0357, 0x0221: 0x035d, 0x0222: 0x0363, 0x0223: 0x0368, - 0x0226: 0x036d, 0x0227: 0x0371, 0x0228: 0x0375, 0x0229: 0x0379, - 0x022a: 0x037d, 0x022b: 0x0381, 0x022c: 0x0385, 0x022d: 0x038b, 0x022e: 0x0391, 0x022f: 0x0396, - 0x0230: 0x039b, 0x0231: 0x039f, 0x0232: 0x03a2, 0x0233: 0x03a5, 0x0234: 0x03a8, 0x0235: 0x03ac, - 0x0238: 0x03b0, 0x0239: 0x03b4, 0x023a: 0x03b8, 0x023b: 0x03be, - 0x023c: 0x03c4, 0x023d: 0x03c9, 0x023e: 0x03ce, 0x023f: 0x03d3, + 0x0200: 0x0e7b, 0x0201: 0x0e80, 0x0202: 0x0e85, 0x0203: 0x0e8c, 0x0204: 0x0e93, 0x0205: 0x0e9a, + 0x0206: 0x0ea1, 0x0207: 0x0ea8, 0x0208: 0x0eaf, 0x0209: 0x0eb4, 0x020a: 0x0eb9, 0x020b: 0x0ec0, + 0x020c: 0x0ec7, 0x020d: 0x0ece, 0x020e: 0x0ed5, 0x020f: 0x0edc, 0x0210: 0x0ee3, 0x0211: 0x0ee8, + 0x0212: 0x0eed, 0x0213: 0x0ef4, 0x0214: 0x0efb, 0x0215: 0x0f02, + 0x0218: 0x0f09, 0x0219: 0x0f0e, 0x021a: 0x0f13, 0x021b: 0x0f1a, 0x021c: 0x0f21, 0x021d: 0x0f28, + 0x0220: 0x0f2f, 0x0221: 0x0f34, 0x0222: 0x0f39, 0x0223: 0x0f40, + 0x0224: 0x0f47, 0x0225: 0x0f4e, 0x0226: 0x0f55, 0x0227: 0x0f5c, 0x0228: 0x0f63, 0x0229: 0x0f68, + 0x022a: 0x0f6d, 0x022b: 0x0f74, 0x022c: 0x0f7b, 0x022d: 0x0f82, 0x022e: 0x0f89, 0x022f: 0x0f90, + 0x0230: 0x0f97, 0x0231: 0x0f9c, 0x0232: 0x0fa1, 0x0233: 0x0fa8, 0x0234: 0x0faf, 0x0235: 0x0fb6, + 0x0236: 0x0fbd, 0x0237: 0x0fc4, 0x0238: 0x0fcb, 0x0239: 0x0fd0, 0x023a: 0x0fd5, 0x023b: 0x0fdc, + 0x023c: 0x0fe3, 0x023d: 0x0fea, 0x023e: 0x0ff1, 0x023f: 0x0ff8, // Block 0x9, offset 0x240 - 0x0240: 0x03d8, 0x0241: 0x03dc, 0x0242: 0x03e0, 0x0243: 0x03e4, 0x0244: 0x03e8, 0x0245: 0x03ec, - 0x0246: 0x03f0, 0x0247: 0x03f4, 0x0248: 0x03f8, 0x0249: 0x03fc, 0x024a: 0x0400, 0x024b: 0x0404, - 0x024c: 0x0408, 0x024d: 0x040c, 0x024e: 0x0410, 0x024f: 0x0414, 0x0250: 0x0418, 0x0251: 0x041c, - 0x0252: 0x0420, 0x0253: 0x0424, 0x0254: 0x0428, 0x0255: 0x042c, 0x0256: 0x0430, 0x0257: 0x0434, - 0x0258: 0x0438, 0x0259: 0x043c, 0x025a: 0x0440, 0x025b: 0x0444, - 0x025e: 0x0448, 0x025f: 0x044c, - 0x0266: 0x0450, 0x0267: 0x0454, 0x0268: 0x0458, 0x0269: 0x045c, - 0x026a: 0x0460, 0x026b: 0x0466, 0x026c: 0x046c, 0x026d: 0x0472, 0x026e: 0x0478, 0x026f: 0x047c, - 0x0270: 0x0480, 0x0271: 0x0486, 0x0272: 0x048c, 0x0273: 0x0490, + 0x0240: 0x0fff, 0x0241: 0x1004, 0x0242: 0x1009, 0x0243: 0x1010, 0x0244: 0x1017, 0x0245: 0x101e, + 0x0248: 0x1025, 0x0249: 0x102a, 0x024a: 0x102f, 0x024b: 0x1036, + 0x024c: 0x103d, 0x024d: 0x1044, 0x0250: 0x104b, 0x0251: 0x1050, + 0x0252: 0x1055, 0x0253: 0x105c, 0x0254: 0x1063, 0x0255: 0x106a, 0x0256: 0x1071, 0x0257: 0x1078, + 0x0259: 0x107f, 0x025b: 0x1084, 0x025d: 0x108b, + 0x025f: 0x1092, 0x0260: 0x1099, 0x0261: 0x109e, 0x0262: 0x10a3, 0x0263: 0x10aa, + 0x0264: 0x10b1, 0x0265: 0x10b8, 0x0266: 0x10bf, 0x0267: 0x10c6, 0x0268: 0x10cd, 0x0269: 0x10d2, + 0x026a: 0x10d7, 0x026b: 0x10de, 0x026c: 0x10e5, 0x026d: 0x10ec, 0x026e: 0x10f3, 0x026f: 0x10fa, + 0x0270: 0x1101, 0x0271: 0x0525, 0x0272: 0x1106, 0x0273: 0x052a, 0x0274: 0x110b, 0x0275: 0x052f, + 0x0276: 0x1110, 0x0277: 0x0534, 0x0278: 0x1115, 0x0279: 0x054a, 0x027a: 0x111a, 0x027b: 0x054f, + 0x027c: 0x111f, 0x027d: 0x0554, // Block 0xa, offset 0x280 - 0x02b0: 0x0494, 0x02b1: 0x0496, 0x02b2: 0x0499, 0x02b3: 0x049b, 0x02b4: 0x049d, 0x02b5: 0x04a0, - 0x02b6: 0x04a3, 0x02b7: 0x04a6, 0x02b8: 0x04a8, + 0x0280: 0x1124, 0x0281: 0x112b, 0x0282: 0x1132, 0x0283: 0x113b, 0x0284: 0x1144, 0x0285: 0x114d, + 0x0286: 0x1156, 0x0287: 0x115f, 0x0288: 0x1168, 0x0289: 0x116f, 0x028a: 0x1176, 0x028b: 0x117f, + 0x028c: 0x1188, 0x028d: 0x1191, 0x028e: 0x119a, 0x028f: 0x11a3, 0x0290: 0x11ac, 0x0291: 0x11b3, + 0x0292: 0x11ba, 0x0293: 0x11c3, 0x0294: 0x11cc, 0x0295: 0x11d5, 0x0296: 0x11de, 0x0297: 0x11e7, + 0x0298: 0x11f0, 0x0299: 0x11f7, 0x029a: 0x11fe, 0x029b: 0x1207, 0x029c: 0x1210, 0x029d: 0x1219, + 0x029e: 0x1222, 0x029f: 0x122b, 0x02a0: 0x1234, 0x02a1: 0x123b, 0x02a2: 0x1242, 0x02a3: 0x124b, + 0x02a4: 0x1254, 0x02a5: 0x125d, 0x02a6: 0x1266, 0x02a7: 0x126f, 0x02a8: 0x1278, 0x02a9: 0x127f, + 0x02aa: 0x1286, 0x02ab: 0x128f, 0x02ac: 0x1298, 0x02ad: 0x12a1, 0x02ae: 0x12aa, 0x02af: 0x12b3, + 0x02b0: 0x12bc, 0x02b1: 0x12c1, 0x02b2: 0x12c6, 0x02b3: 0x12cd, 0x02b4: 0x12d2, + 0x02b6: 0x12d9, 0x02b7: 0x12de, 0x02b8: 0x12e5, 0x02b9: 0x12ea, 0x02ba: 0x12ef, 0x02bb: 0x04ee, + 0x02bc: 0x12f4, 0x02bd: 0x12f9, 0x02be: 0x12fd, 0x02bf: 0x12f9, // Block 0xb, offset 0x2c0 - 0x02d8: 0x04aa, 0x02d9: 0x04ae, 0x02da: 0x04b2, 0x02db: 0x04b6, 0x02dc: 0x04ba, 0x02dd: 0x04be, - 0x02e0: 0x04c2, 0x02e1: 0x04c5, 0x02e2: 0x02c8, 0x02e3: 0x04c7, - 0x02e4: 0x04c9, + 0x02c0: 0x1300, 0x02c1: 0x1309, 0x02c2: 0x130f, 0x02c3: 0x1316, 0x02c4: 0x131b, + 0x02c6: 0x1322, 0x02c7: 0x1327, 0x02c8: 0x132e, 0x02c9: 0x04f6, 0x02ca: 0x1333, 0x02cb: 0x04fb, + 0x02cc: 0x1338, 0x02cd: 0x1343, 0x02ce: 0x134f, 0x02cf: 0x135b, 0x02d0: 0x1361, 0x02d1: 0x1366, + 0x02d2: 0x136b, 0x02d3: 0x0514, 0x02d6: 0x1372, 0x02d7: 0x1377, + 0x02d8: 0x137e, 0x02d9: 0x1383, 0x02da: 0x1388, 0x02db: 0x0500, 0x02dd: 0x1393, + 0x02de: 0x139f, 0x02df: 0x13ab, 0x02e0: 0x13b1, 0x02e1: 0x13b6, 0x02e2: 0x13bb, 0x02e3: 0x0539, + 0x02e4: 0x13c2, 0x02e5: 0x13c7, 0x02e6: 0x13cc, 0x02e7: 0x13d1, 0x02e8: 0x13d8, 0x02e9: 0x13dd, + 0x02ea: 0x13e2, 0x02eb: 0x050a, 0x02ec: 0x13e7, 0x02ed: 0x13f1, 0x02ee: 0x04e8, 0x02ef: 0x13f7, + 0x02f2: 0x13f9, 0x02f3: 0x1400, 0x02f4: 0x1405, + 0x02f6: 0x140c, 0x02f7: 0x1411, 0x02f8: 0x1418, 0x02f9: 0x0505, 0x02fa: 0x141d, 0x02fb: 0x050f, + 0x02fc: 0x1422, 0x02fd: 0x0011, 0x02fe: 0x142a, // Block 0xc, offset 0x300 - 0x0300: 0x04cc, 0x0301: 0x04cf, 0x0303: 0x04d2, 0x0304: 0x04d5, - 0x0334: 0x04da, - 0x033a: 0x04dd, - 0x033e: 0x04e1, + 0x0300: 0x1486, 0x0301: 0x001c, 0x0302: 0x000d, 0x0303: 0x000f, 0x0304: 0x1488, 0x0305: 0x148a, + 0x0306: 0x148c, 0x0307: 0x148e, 0x0308: 0x1490, 0x0309: 0x1492, 0x030a: 0x1494, 0x030b: 0x1496, + 0x030c: 0x149a, 0x030d: 0x149c, 0x030e: 0x149e, 0x0310: 0x0007, 0x0311: 0x0941, + 0x0312: 0x001e, 0x0313: 0x04c7, 0x0314: 0x0943, 0x0315: 0x0494, 0x0316: 0x094e, 0x0317: 0x04c5, + 0x0318: 0x0950, 0x0319: 0x14a0, 0x031a: 0x0960, 0x031b: 0x02c8, 0x031c: 0x0962, + 0x0328: 0x14a2, // Block 0xd, offset 0x340 - 0x0344: 0x0011, 0x0345: 0x04e8, - 0x0346: 0x04ee, 0x0347: 0x04f3, 0x0348: 0x04f6, 0x0349: 0x04fb, 0x034a: 0x0500, - 0x034c: 0x0505, 0x034e: 0x050a, 0x034f: 0x050f, 0x0350: 0x0514, - 0x036a: 0x051b, 0x036b: 0x0520, 0x036c: 0x0525, 0x036d: 0x052a, 0x036e: 0x052f, 0x036f: 0x0534, - 0x0370: 0x0539, + 0x0340: 0x14a5, 0x0341: 0x14a9, 0x0342: 0x14ad, 0x0343: 0x14af, 0x0345: 0x14b3, + 0x0346: 0x14b7, 0x0347: 0x14bb, 0x0349: 0x14be, 0x034a: 0x094c, 0x034b: 0x0916, + 0x034c: 0x0916, 0x034d: 0x0916, 0x034e: 0x0494, 0x034f: 0x14c2, 0x0350: 0x0918, 0x0351: 0x0918, + 0x0352: 0x091e, 0x0353: 0x04c5, 0x0355: 0x0922, 0x0356: 0x14c5, + 0x0359: 0x0929, 0x035a: 0x14c8, 0x035b: 0x092b, 0x035c: 0x092b, 0x035d: 0x092b, + 0x0360: 0x14ca, 0x0361: 0x14cd, 0x0362: 0x14d1, + 0x0364: 0x14d4, 0x0366: 0x14d6, 0x0368: 0x14d4, + 0x036a: 0x091c, 0x036b: 0x0046, 0x036c: 0x090b, 0x036d: 0x14ad, 0x036f: 0x0941, + 0x0370: 0x090f, 0x0371: 0x14d9, 0x0373: 0x0920, 0x0374: 0x001e, 0x0375: 0x14db, + 0x0376: 0x14de, 0x0377: 0x14e1, 0x0378: 0x14e4, 0x0379: 0x097c, 0x037b: 0x14e7, + 0x037c: 0x056f, 0x037d: 0x0973, 0x037e: 0x14eb, 0x037f: 0x14ee, // Block 0xe, offset 0x380 - 0x038a: 0x0540, 0x038b: 0x0545, - 0x038c: 0x054a, 0x038d: 0x054f, 0x038e: 0x0554, 0x0390: 0x0559, 0x0391: 0x055c, - 0x0392: 0x055f, 0x0393: 0x050a, 0x0394: 0x0520, 0x0395: 0x056c, 0x0396: 0x056f, - 0x03b0: 0x0572, 0x03b1: 0x0575, 0x03b2: 0x0578, 0x03b4: 0x057b, 0x03b5: 0x057e, - 0x03b9: 0x0581, + 0x0380: 0x14f1, 0x0385: 0x090d, + 0x0386: 0x093f, 0x0387: 0x0941, 0x0388: 0x097c, 0x0389: 0x0499, + 0x0390: 0x14f5, 0x0391: 0x14fb, + 0x0392: 0x1501, 0x0393: 0x1508, 0x0394: 0x150e, 0x0395: 0x1514, 0x0396: 0x151a, 0x0397: 0x1520, + 0x0398: 0x1526, 0x0399: 0x152c, 0x039a: 0x1532, 0x039b: 0x1538, 0x039c: 0x153e, 0x039d: 0x1544, + 0x039e: 0x154a, 0x039f: 0x1550, 0x03a0: 0x0918, 0x03a1: 0x1555, 0x03a2: 0x1558, 0x03a3: 0x155c, + 0x03a4: 0x155f, 0x03a5: 0x1561, 0x03a6: 0x1564, 0x03a7: 0x1568, 0x03a8: 0x156d, 0x03a9: 0x1570, + 0x03aa: 0x1572, 0x03ab: 0x1575, 0x03ac: 0x091e, 0x03ad: 0x14ad, 0x03ae: 0x090d, 0x03af: 0x0920, + 0x03b0: 0x097c, 0x03b1: 0x1579, 0x03b2: 0x157c, 0x03b3: 0x1580, 0x03b4: 0x096d, 0x03b5: 0x1583, + 0x03b6: 0x1586, 0x03b7: 0x158a, 0x03b8: 0x158f, 0x03b9: 0x04c7, 0x03ba: 0x1592, 0x03bb: 0x1595, + 0x03bc: 0x04c5, 0x03bd: 0x0984, 0x03be: 0x093f, 0x03bf: 0x0950, // Block 0xf, offset 0x3c0 - 0x03c0: 0x0584, 0x03c1: 0x0589, 0x03c3: 0x058e, - 0x03c7: 0x0593, - 0x03cc: 0x0598, 0x03cd: 0x059d, 0x03ce: 0x05a2, - 0x03d9: 0x05a7, - 0x03f9: 0x05ac, + 0x03e0: 0x001c, 0x03e1: 0x000d, 0x03e2: 0x000f, 0x03e3: 0x1488, + 0x03e4: 0x148a, 0x03e5: 0x148c, 0x03e6: 0x148e, 0x03e7: 0x1490, 0x03e8: 0x1492, 0x03e9: 0x16cb, + 0x03ea: 0x16ce, 0x03eb: 0x16d1, 0x03ec: 0x16d4, 0x03ed: 0x16d7, 0x03ee: 0x16da, 0x03ef: 0x16dd, + 0x03f0: 0x16e0, 0x03f1: 0x16e3, 0x03f2: 0x16e6, 0x03f3: 0x16e9, 0x03f4: 0x16ec, 0x03f5: 0x16f0, + 0x03f6: 0x16f4, 0x03f7: 0x16f8, 0x03f8: 0x16fc, 0x03f9: 0x1700, 0x03fa: 0x1704, 0x03fb: 0x1708, + 0x03fc: 0x170c, 0x03fd: 0x1710, 0x03fe: 0x1715, 0x03ff: 0x171a, // Block 0x10, offset 0x400 - 0x0410: 0x05b1, 0x0411: 0x05b6, - 0x0413: 0x05bb, 0x0417: 0x05c0, - 0x041c: 0x05c5, 0x041d: 0x05ca, - 0x041e: 0x05cf, - 0x0436: 0x05d4, 0x0437: 0x05d9, + 0x0400: 0x171f, 0x0401: 0x1724, 0x0402: 0x1729, 0x0403: 0x172e, 0x0404: 0x1733, 0x0405: 0x1738, + 0x0406: 0x173d, 0x0407: 0x1742, 0x0408: 0x1747, 0x0409: 0x174a, 0x040a: 0x174d, 0x040b: 0x1750, + 0x040c: 0x1753, 0x040d: 0x1756, 0x040e: 0x1759, 0x040f: 0x175c, 0x0410: 0x175f, 0x0411: 0x1762, + 0x0412: 0x1766, 0x0413: 0x176a, 0x0414: 0x176e, 0x0415: 0x1772, 0x0416: 0x1776, 0x0417: 0x177a, + 0x0418: 0x177e, 0x0419: 0x1782, 0x041a: 0x1786, 0x041b: 0x178a, 0x041c: 0x178e, 0x041d: 0x1792, + 0x041e: 0x1796, 0x041f: 0x179a, 0x0420: 0x179e, 0x0421: 0x17a2, 0x0422: 0x17a6, 0x0423: 0x17aa, + 0x0424: 0x17ae, 0x0425: 0x17b2, 0x0426: 0x17b6, 0x0427: 0x17ba, 0x0428: 0x17be, 0x0429: 0x17c2, + 0x042a: 0x17c6, 0x042b: 0x17ca, 0x042c: 0x17ce, 0x042d: 0x17d2, 0x042e: 0x17d6, 0x042f: 0x17da, + 0x0430: 0x17de, 0x0431: 0x17e2, 0x0432: 0x17e6, 0x0433: 0x17ea, 0x0434: 0x17ee, 0x0435: 0x17f2, + 0x0436: 0x0906, 0x0437: 0x090b, 0x0438: 0x14ad, 0x0439: 0x090d, 0x043a: 0x090f, 0x043b: 0x14d9, + 0x043c: 0x0914, 0x043d: 0x0916, 0x043e: 0x0918, 0x043f: 0x091a, // Block 0x11, offset 0x440 - 0x0441: 0x05de, 0x0442: 0x05e3, - 0x0450: 0x05e8, 0x0451: 0x05ed, - 0x0452: 0x05f2, 0x0453: 0x05f7, 0x0456: 0x05fc, 0x0457: 0x0601, - 0x045a: 0x0606, 0x045b: 0x060b, 0x045c: 0x0610, 0x045d: 0x0615, - 0x045e: 0x061a, 0x045f: 0x061f, 0x0462: 0x0624, 0x0463: 0x0629, - 0x0464: 0x062e, 0x0465: 0x0633, 0x0466: 0x0638, 0x0467: 0x063d, - 0x046a: 0x0642, 0x046b: 0x0647, 0x046c: 0x064c, 0x046d: 0x0651, 0x046e: 0x0656, 0x046f: 0x065b, - 0x0470: 0x0660, 0x0471: 0x0665, 0x0472: 0x066a, 0x0473: 0x066f, 0x0474: 0x0674, 0x0475: 0x0679, - 0x0478: 0x067e, 0x0479: 0x0683, + 0x0440: 0x091c, 0x0441: 0x091e, 0x0442: 0x0920, 0x0443: 0x0922, 0x0444: 0x0924, 0x0445: 0x0929, + 0x0446: 0x14c8, 0x0447: 0x092b, 0x0448: 0x17f6, 0x0449: 0x092d, 0x044a: 0x092f, 0x044b: 0x155f, + 0x044c: 0x0931, 0x044d: 0x1570, 0x044e: 0x17f8, 0x044f: 0x14d4, 0x0450: 0x0007, 0x0451: 0x093d, + 0x0452: 0x0984, 0x0453: 0x093f, 0x0454: 0x0941, 0x0455: 0x098c, 0x0456: 0x094c, 0x0457: 0x0494, + 0x0458: 0x097c, 0x0459: 0x0499, 0x045a: 0x094e, 0x045b: 0x04c5, 0x045c: 0x0950, 0x045d: 0x14a0, + 0x045e: 0x001e, 0x045f: 0x0960, 0x0460: 0x17fa, 0x0461: 0x049b, 0x0462: 0x02c8, 0x0463: 0x0962, + 0x0464: 0x0964, 0x0465: 0x096d, 0x0466: 0x04a6, 0x0467: 0x04c7, 0x0468: 0x04a8, 0x0469: 0x09df, + 0x046a: 0x1486, // Block 0x12, offset 0x480 - 0x0487: 0x0688, + 0x048c: 0x1b8a, 0x048e: 0x1b91, 0x0490: 0x1b98, + 0x0492: 0x1b9f, 0x0494: 0x1ba6, 0x0496: 0x1bad, + 0x0498: 0x1bb4, 0x049a: 0x1bbb, 0x049c: 0x1bc2, + 0x049e: 0x1bc9, 0x04a0: 0x1bd0, 0x04a2: 0x1bd7, + 0x04a5: 0x1bde, 0x04a7: 0x1be5, 0x04a9: 0x1bec, + 0x04b0: 0x1bf3, 0x04b1: 0x1bfa, 0x04b3: 0x1c01, 0x04b4: 0x1c08, + 0x04b6: 0x1c0f, 0x04b7: 0x1c16, 0x04b9: 0x1c1d, 0x04ba: 0x1c24, + 0x04bc: 0x1c2b, 0x04bd: 0x1c32, // Block 0x13, offset 0x4c0 - 0x04e2: 0x068d, 0x04e3: 0x0692, - 0x04e4: 0x0697, 0x04e5: 0x069c, 0x04e6: 0x06a1, + 0x04c0: 0x1ed8, 0x04c1: 0x1ede, 0x04c2: 0x1ee4, 0x04c3: 0x1eea, 0x04c4: 0x1ef0, 0x04c5: 0x1ef6, + 0x04c6: 0x1efc, 0x04c7: 0x1f02, 0x04c8: 0x1f08, 0x04c9: 0x1f0e, 0x04ca: 0x1f14, 0x04cb: 0x1f1a, + 0x04cc: 0x1f20, 0x04cd: 0x1f26, 0x04ce: 0x1f2c, 0x04cf: 0x1f35, 0x04d0: 0x1f3e, 0x04d1: 0x1f47, + 0x04d2: 0x1f50, 0x04d3: 0x1f59, 0x04d4: 0x1f62, 0x04d5: 0x1f6b, 0x04d6: 0x1f74, 0x04d7: 0x1f7d, + 0x04d8: 0x1f86, 0x04d9: 0x1f8f, 0x04da: 0x1f98, 0x04db: 0x1fa1, 0x04dc: 0x1faa, 0x04dd: 0x1fb3, + 0x04de: 0x1fc5, 0x04e0: 0x1fd4, 0x04e1: 0x1fda, 0x04e2: 0x1fe0, 0x04e3: 0x1fe6, + 0x04e4: 0x1fec, 0x04e5: 0x1ff2, 0x04e6: 0x1ff8, 0x04e7: 0x1ffe, 0x04e8: 0x2004, 0x04e9: 0x200a, + 0x04ea: 0x2010, 0x04eb: 0x2016, 0x04ec: 0x201c, 0x04ed: 0x2022, 0x04ee: 0x2028, 0x04ef: 0x202e, + 0x04f0: 0x2034, 0x04f1: 0x203a, 0x04f2: 0x2040, 0x04f3: 0x2046, 0x04f4: 0x204c, 0x04f5: 0x2052, + 0x04f6: 0x2058, 0x04f7: 0x205e, 0x04f8: 0x2064, 0x04f9: 0x206a, 0x04fa: 0x2070, 0x04fb: 0x2076, + 0x04fc: 0x207c, 0x04fd: 0x2082, 0x04fe: 0x2088, 0x04ff: 0x208e, // Block 0x14, offset 0x500 - 0x0535: 0x06a6, - 0x0536: 0x06ab, 0x0537: 0x06b0, 0x0538: 0x06b5, + 0x0500: 0x2094, 0x0501: 0x209a, 0x0502: 0x20a0, 0x0503: 0x20a6, 0x0504: 0x20ac, 0x0505: 0x20b0, + 0x0506: 0x192e, 0x0507: 0x20b4, + 0x0510: 0x20b8, 0x0511: 0x20bc, + 0x0512: 0x20bf, 0x0513: 0x20c2, 0x0514: 0x20c5, 0x0515: 0x20c8, 0x0516: 0x20cb, 0x0517: 0x20ce, + 0x0518: 0x20d1, 0x0519: 0x20d4, 0x051a: 0x20d7, 0x051b: 0x20da, 0x051c: 0x20dd, 0x051d: 0x20e0, + 0x051e: 0x20e3, 0x051f: 0x20e6, 0x0520: 0x1d38, 0x0521: 0x1d44, 0x0522: 0x1d50, 0x0523: 0x1d58, + 0x0524: 0x1d78, 0x0525: 0x1d7c, 0x0526: 0x1d88, 0x0527: 0x1d90, 0x0528: 0x1d94, 0x0529: 0x1d9c, + 0x052a: 0x1da0, 0x052b: 0x1da4, 0x052c: 0x1da8, 0x052d: 0x1dac, 0x052e: 0x20e9, 0x052f: 0x20f0, + 0x0530: 0x20f7, 0x0531: 0x20fe, 0x0532: 0x2105, 0x0533: 0x210c, 0x0534: 0x2113, 0x0535: 0x211a, + 0x0536: 0x2121, 0x0537: 0x2128, 0x0538: 0x212f, 0x0539: 0x2136, 0x053a: 0x213d, 0x053b: 0x2144, + 0x053c: 0x214b, 0x053d: 0x215b, 0x053e: 0x2168, // Block 0x15, offset 0x540 - 0x0540: 0x06ba, 0x0542: 0x06bf, - 0x0553: 0x06c4, + 0x0540: 0x1826, 0x0541: 0x183e, 0x0542: 0x1eb0, 0x0543: 0x1eb4, 0x0544: 0x216f, 0x0545: 0x2173, + 0x0546: 0x2177, 0x0547: 0x1852, 0x0548: 0x217b, 0x0549: 0x1882, 0x054a: 0x194a, 0x054b: 0x197a, + 0x054c: 0x1976, 0x054d: 0x194e, 0x054e: 0x1abe, 0x054f: 0x18a2, 0x0550: 0x1942, 0x0551: 0x217f, + 0x0552: 0x2183, 0x0553: 0x2187, 0x0554: 0x218b, 0x0555: 0x218f, 0x0556: 0x2193, 0x0557: 0x2197, + 0x0558: 0x219b, 0x0559: 0x219f, 0x055a: 0x21a3, 0x055b: 0x18ba, 0x055c: 0x21a7, 0x055d: 0x21ab, + 0x055e: 0x21af, 0x055f: 0x21b3, 0x0560: 0x21b7, 0x0561: 0x21bb, 0x0562: 0x21bf, 0x0563: 0x21c3, + 0x0564: 0x1eb8, 0x0565: 0x1ebc, 0x0566: 0x1ec0, 0x0567: 0x21c7, 0x0568: 0x21cb, 0x0569: 0x21cf, + 0x056a: 0x21d3, 0x056b: 0x21d7, 0x056c: 0x21db, 0x056d: 0x21df, 0x056e: 0x21e3, 0x056f: 0x21e7, + 0x0570: 0x21eb, 0x0571: 0x21ef, 0x0572: 0x21f2, 0x0573: 0x21f5, 0x0574: 0x21f8, 0x0575: 0x21fb, + 0x0576: 0x21fe, 0x0577: 0x2201, 0x0578: 0x2204, 0x0579: 0x2207, 0x057a: 0x220a, 0x057b: 0x220d, + 0x057c: 0x2210, 0x057d: 0x2213, 0x057e: 0x2216, 0x057f: 0x2219, // Block 0x16, offset 0x580 - 0x05a9: 0x06c9, - 0x05b1: 0x06d0, 0x05b4: 0x06d7, + 0x0580: 0x2325, 0x0581: 0x2335, 0x0582: 0x2342, 0x0583: 0x2352, 0x0584: 0x235c, 0x0585: 0x236c, + 0x0586: 0x2376, 0x0587: 0x2380, 0x0588: 0x2393, 0x0589: 0x23a0, 0x058a: 0x23aa, 0x058b: 0x23b4, + 0x058c: 0x23be, 0x058d: 0x23cb, 0x058e: 0x23d8, 0x058f: 0x23e5, 0x0590: 0x23f2, 0x0591: 0x23ff, + 0x0592: 0x240c, 0x0593: 0x2419, 0x0594: 0x242c, 0x0595: 0x2433, 0x0596: 0x2446, 0x0597: 0x2459, + 0x0598: 0x2469, 0x0599: 0x2476, 0x059a: 0x2489, 0x059b: 0x249c, 0x059c: 0x24a9, 0x059d: 0x24b3, + 0x059e: 0x24bd, 0x059f: 0x24ca, 0x05a0: 0x24d7, 0x05a1: 0x24e7, 0x05a2: 0x24f7, 0x05a3: 0x2501, + 0x05a4: 0x250b, 0x05a5: 0x2518, 0x05a6: 0x2522, 0x05a7: 0x252c, 0x05a8: 0x2533, 0x05a9: 0x253a, + 0x05aa: 0x2544, 0x05ab: 0x254e, 0x05ac: 0x2561, 0x05ad: 0x256e, 0x05ae: 0x257e, 0x05af: 0x2591, + 0x05b0: 0x259e, 0x05b1: 0x25a8, 0x05b2: 0x25b2, 0x05b3: 0x25c5, 0x05b4: 0x25d2, 0x05b5: 0x25e5, + 0x05b6: 0x25ef, 0x05b7: 0x25ff, 0x05b8: 0x2609, 0x05b9: 0x2616, 0x05ba: 0x2620, 0x05bb: 0x262d, + 0x05bc: 0x263d, 0x05bd: 0x264a, 0x05be: 0x265a, 0x05bf: 0x2667, // Block 0x17, offset 0x5c0 - 0x05d8: 0x06de, 0x05d9: 0x06e5, 0x05da: 0x06ec, 0x05db: 0x06f3, 0x05dc: 0x06fa, 0x05dd: 0x0701, - 0x05de: 0x0708, 0x05df: 0x070f, + 0x05c0: 0x266e, 0x05c1: 0x267e, 0x05c2: 0x2688, 0x05c3: 0x2692, 0x05c4: 0x269f, 0x05c5: 0x26a9, + 0x05c6: 0x26b3, 0x05c7: 0x26bd, 0x05c8: 0x26cd, 0x05c9: 0x26da, 0x05ca: 0x26e1, 0x05cb: 0x26f4, + 0x05cc: 0x26fe, 0x05cd: 0x270e, 0x05ce: 0x271b, 0x05cf: 0x2728, 0x05d0: 0x2732, 0x05d1: 0x273c, + 0x05d2: 0x2749, 0x05d3: 0x2750, 0x05d4: 0x275d, 0x05d5: 0x276d, 0x05d6: 0x2774, 0x05d7: 0x2787, + 0x05d8: 0x2791, 0x05d9: 0x2796, 0x05da: 0x279b, 0x05db: 0x27a0, 0x05dc: 0x27a5, 0x05dd: 0x27aa, + 0x05de: 0x27af, 0x05df: 0x27b4, 0x05e0: 0x27b9, 0x05e1: 0x27be, 0x05e2: 0x27c3, 0x05e3: 0x27c9, + 0x05e4: 0x27cf, 0x05e5: 0x27d5, 0x05e6: 0x27db, 0x05e7: 0x27e1, 0x05e8: 0x27e7, 0x05e9: 0x27ed, + 0x05ea: 0x27f3, 0x05eb: 0x27f9, 0x05ec: 0x27ff, 0x05ed: 0x2805, 0x05ee: 0x280b, 0x05ef: 0x2811, + 0x05f0: 0x2817, 0x05f1: 0x281d, 0x05f2: 0x2821, 0x05f3: 0x2824, 0x05f4: 0x2827, 0x05f5: 0x282b, + 0x05f6: 0x282e, 0x05f7: 0x2831, 0x05f8: 0x2834, 0x05f9: 0x2838, 0x05fa: 0x283c, 0x05fb: 0x283f, + 0x05fc: 0x2846, 0x05fd: 0x284d, 0x05fe: 0x2854, 0x05ff: 0x285b, // Block 0x18, offset 0x600 - 0x060b: 0x0716, - 0x060c: 0x071d, - 0x061c: 0x0724, 0x061d: 0x072b, - 0x061f: 0x0732, + 0x0600: 0x2868, 0x0601: 0x286b, 0x0602: 0x286e, 0x0603: 0x2872, 0x0604: 0x2875, 0x0605: 0x2878, + 0x0606: 0x287b, 0x0607: 0x287e, 0x0608: 0x2881, 0x0609: 0x2885, 0x060a: 0x288a, 0x060b: 0x288d, + 0x060c: 0x2890, 0x060d: 0x2894, 0x060e: 0x2898, 0x060f: 0x289b, 0x0610: 0x289e, 0x0611: 0x28a1, + 0x0612: 0x28a5, 0x0613: 0x28a9, 0x0614: 0x28ad, 0x0615: 0x28b1, 0x0616: 0x28b5, 0x0617: 0x28b8, + 0x0618: 0x28bb, 0x0619: 0x28be, 0x061a: 0x28c1, 0x061b: 0x28c4, 0x061c: 0x28c8, 0x061d: 0x28cb, + 0x061e: 0x28ce, 0x061f: 0x28d1, 0x0620: 0x28d5, 0x0621: 0x28d9, 0x0622: 0x28dc, 0x0623: 0x28e0, + 0x0624: 0x28e4, 0x0625: 0x28e8, 0x0626: 0x28eb, 0x0627: 0x28ef, 0x0628: 0x28f5, 0x0629: 0x28fc, + 0x062a: 0x28ff, 0x062b: 0x2903, 0x062c: 0x2907, 0x062d: 0x290b, 0x062e: 0x290f, 0x062f: 0x2917, + 0x0630: 0x2920, 0x0631: 0x2923, 0x0632: 0x2926, 0x0633: 0x292a, 0x0634: 0x292d, 0x0635: 0x2930, + 0x0636: 0x2933, 0x0637: 0x2937, 0x0638: 0x293a, 0x0639: 0x293d, 0x063a: 0x2940, 0x063b: 0x2943, + 0x063c: 0x2946, 0x063d: 0x294a, 0x063e: 0x294d, 0x063f: 0x2950, // Block 0x19, offset 0x640 - 0x0673: 0x0739, - 0x0676: 0x0740, + 0x0640: 0x2953, 0x0641: 0x2957, 0x0642: 0x295b, 0x0643: 0x2960, 0x0644: 0x2963, 0x0645: 0x2966, + 0x0646: 0x2969, 0x0647: 0x2970, 0x0648: 0x2974, 0x0649: 0x2977, 0x064a: 0x297a, 0x064b: 0x297d, + 0x064c: 0x2980, 0x064d: 0x2983, 0x064e: 0x2986, 0x064f: 0x2989, 0x0650: 0x298c, 0x0651: 0x298f, + 0x0652: 0x2992, 0x0653: 0x2996, 0x0654: 0x2999, 0x0655: 0x299c, 0x0656: 0x29a0, 0x0657: 0x29a4, + 0x0658: 0x29a7, 0x0659: 0x29ac, 0x065a: 0x29b0, 0x065b: 0x29b3, 0x065c: 0x29b6, 0x065d: 0x29b9, + 0x065e: 0x29bc, 0x065f: 0x29c2, 0x0660: 0x29c8, 0x0661: 0x29cd, 0x0662: 0x29d2, 0x0663: 0x29d7, + 0x0664: 0x29dc, 0x0665: 0x29e1, 0x0666: 0x29e6, 0x0667: 0x29eb, 0x0668: 0x29f0, 0x0669: 0x29f5, + 0x066a: 0x29fb, 0x066b: 0x2a01, 0x066c: 0x2a07, 0x066d: 0x2a0d, 0x066e: 0x2a13, 0x066f: 0x2a19, + 0x0670: 0x2a1f, 0x0671: 0x2a25, 0x0672: 0x2a2b, 0x0673: 0x2a31, 0x0674: 0x2a37, 0x0675: 0x2a3d, + 0x0676: 0x2a43, 0x0677: 0x2a49, 0x0678: 0x2a4f, 0x0679: 0x2a55, 0x067a: 0x2a5b, 0x067b: 0x2a61, + 0x067c: 0x2a67, 0x067d: 0x2a6d, 0x067e: 0x2a73, 0x067f: 0x2a79, // Block 0x1a, offset 0x680 - 0x0699: 0x0747, 0x069a: 0x074e, 0x069b: 0x0755, - 0x069e: 0x075c, + 0x0680: 0x2fce, 0x0681: 0x2fd2, 0x0682: 0x2fd6, 0x0683: 0x2fda, 0x0684: 0x2fde, 0x0685: 0x2fe2, + 0x0686: 0x2fe6, 0x0687: 0x2fea, 0x0688: 0x2fee, 0x0689: 0x2eed, 0x068a: 0x2ff2, 0x068b: 0x2ef1, + 0x068c: 0x2ff6, 0x068d: 0x2ffa, 0x068e: 0x2ffe, 0x068f: 0x3002, 0x0690: 0x3006, 0x0691: 0x2e6d, + 0x0692: 0x2b15, 0x0693: 0x300a, 0x0694: 0x300e, 0x0695: 0x195a, 0x0696: 0x2c25, 0x0697: 0x2d71, + 0x0698: 0x3012, 0x0699: 0x3016, 0x069a: 0x2f0d, 0x069b: 0x301a, 0x069c: 0x2f11, 0x069d: 0x301e, + 0x069e: 0x3022, 0x069f: 0x3026, 0x06a0: 0x2e75, 0x06a1: 0x302a, 0x06a2: 0x302e, 0x06a3: 0x3032, + 0x06a4: 0x3036, 0x06a5: 0x303a, 0x06a6: 0x2e79, 0x06a7: 0x303e, 0x06a8: 0x3042, 0x06a9: 0x3046, + 0x06aa: 0x304a, 0x06ab: 0x304e, 0x06ac: 0x3052, 0x06ad: 0x2f41, 0x06ae: 0x3056, 0x06af: 0x305a, + 0x06b0: 0x2cb1, 0x06b1: 0x305e, 0x06b2: 0x2f51, 0x06b3: 0x3062, 0x06b4: 0x3066, 0x06b5: 0x306a, + 0x06b6: 0x306e, 0x06b7: 0x3072, 0x06b8: 0x2f65, 0x06b9: 0x3076, 0x06ba: 0x2e99, 0x06bb: 0x307a, + 0x06bc: 0x2f69, 0x06bd: 0x2bd9, 0x06be: 0x307e, 0x06bf: 0x2f6d, // Block 0x1b, offset 0x6c0 - 0x06c8: 0x0763, 0x06cb: 0x076a, - 0x06cc: 0x0771, - 0x06dc: 0x0778, 0x06dd: 0x077f, + 0x06c0: 0x3082, 0x06c1: 0x2f75, 0x06c2: 0x3086, 0x06c3: 0x308a, 0x06c4: 0x308e, 0x06c5: 0x3092, + 0x06c6: 0x3096, 0x06c7: 0x2f7d, 0x06c8: 0x2e8d, 0x06c9: 0x309a, 0x06ca: 0x2f81, 0x06cb: 0x309e, + 0x06cc: 0x2f85, 0x06cd: 0x30a2, 0x06ce: 0x1b76, 0x06cf: 0x30a6, 0x06d0: 0x30ab, 0x06d1: 0x30b0, + 0x06d2: 0x30b5, 0x06d3: 0x30b9, 0x06d4: 0x30bd, 0x06d5: 0x30c1, 0x06d6: 0x30c6, 0x06d7: 0x30cb, + 0x06d8: 0x30d0, 0x06d9: 0x30d4, // Block 0x1c, offset 0x700 - 0x0714: 0x0786, + 0x0700: 0x30d8, 0x0701: 0x30db, 0x0702: 0x30de, 0x0703: 0x30e1, 0x0704: 0x30e5, 0x0705: 0x30e9, + 0x0706: 0x30e9, + 0x0713: 0x30ec, 0x0714: 0x30f1, 0x0715: 0x30f6, 0x0716: 0x30fb, 0x0717: 0x3100, + 0x071d: 0x3105, + 0x071f: 0x310a, 0x0720: 0x310f, 0x0721: 0x14db, 0x0722: 0x14e4, 0x0723: 0x3112, + 0x0724: 0x3115, 0x0725: 0x3118, 0x0726: 0x311b, 0x0727: 0x311e, 0x0728: 0x3121, 0x0729: 0x1494, + 0x072a: 0x3124, 0x072b: 0x3129, 0x072c: 0x312e, 0x072d: 0x3135, 0x072e: 0x313c, 0x072f: 0x3141, + 0x0730: 0x3146, 0x0731: 0x314b, 0x0732: 0x3150, 0x0733: 0x3155, 0x0734: 0x315a, 0x0735: 0x315f, + 0x0736: 0x3164, 0x0738: 0x3169, 0x0739: 0x316e, 0x073a: 0x3173, 0x073b: 0x3178, + 0x073c: 0x317d, 0x073e: 0x3182, // Block 0x1d, offset 0x740 - 0x074a: 0x078d, 0x074b: 0x0794, - 0x074c: 0x079b, + 0x0740: 0x3187, 0x0741: 0x318c, 0x0743: 0x3191, 0x0744: 0x3196, + 0x0746: 0x319b, 0x0747: 0x31a0, 0x0748: 0x31a5, 0x0749: 0x31aa, 0x074a: 0x31af, 0x074b: 0x31b4, + 0x074c: 0x31b9, 0x074d: 0x31be, 0x074e: 0x31c3, 0x074f: 0x31c8, 0x0750: 0x31cd, 0x0751: 0x31cd, + 0x0752: 0x31d0, 0x0753: 0x31d0, 0x0754: 0x31d0, 0x0755: 0x31d0, 0x0756: 0x31d3, 0x0757: 0x31d3, + 0x0758: 0x31d3, 0x0759: 0x31d3, 0x075a: 0x31d6, 0x075b: 0x31d6, 0x075c: 0x31d6, 0x075d: 0x31d6, + 0x075e: 0x31d9, 0x075f: 0x31d9, 0x0760: 0x31d9, 0x0761: 0x31d9, 0x0762: 0x31dc, 0x0763: 0x31dc, + 0x0764: 0x31dc, 0x0765: 0x31dc, 0x0766: 0x31df, 0x0767: 0x31df, 0x0768: 0x31df, 0x0769: 0x31df, + 0x076a: 0x31e2, 0x076b: 0x31e2, 0x076c: 0x31e2, 0x076d: 0x31e2, 0x076e: 0x31e5, 0x076f: 0x31e5, + 0x0770: 0x31e5, 0x0771: 0x31e5, 0x0772: 0x31e8, 0x0773: 0x31e8, 0x0774: 0x31e8, 0x0775: 0x31e8, + 0x0776: 0x31eb, 0x0777: 0x31eb, 0x0778: 0x31eb, 0x0779: 0x31eb, 0x077a: 0x31ee, 0x077b: 0x31ee, + 0x077c: 0x31ee, 0x077d: 0x31ee, 0x077e: 0x31f1, 0x077f: 0x31f1, // Block 0x1e, offset 0x780 - 0x0788: 0x07a2, + 0x0780: 0x31f1, 0x0781: 0x31f1, 0x0782: 0x31f4, 0x0783: 0x31f4, 0x0784: 0x31f7, 0x0785: 0x31f7, + 0x0786: 0x31fa, 0x0787: 0x31fa, 0x0788: 0x31fd, 0x0789: 0x31fd, 0x078a: 0x3200, 0x078b: 0x3200, + 0x078c: 0x3203, 0x078d: 0x3203, 0x078e: 0x3206, 0x078f: 0x3206, 0x0790: 0x3206, 0x0791: 0x3206, + 0x0792: 0x3209, 0x0793: 0x3209, 0x0794: 0x3209, 0x0795: 0x3209, 0x0796: 0x320c, 0x0797: 0x320c, + 0x0798: 0x320c, 0x0799: 0x320c, 0x079a: 0x320f, 0x079b: 0x320f, 0x079c: 0x320f, 0x079d: 0x320f, + 0x079e: 0x3212, 0x079f: 0x3212, 0x07a0: 0x3215, 0x07a1: 0x3215, 0x07a2: 0x3215, 0x07a3: 0x3215, + 0x07a4: 0x06ba, 0x07a5: 0x06ba, 0x07a6: 0x3218, 0x07a7: 0x3218, 0x07a8: 0x3218, 0x07a9: 0x3218, + 0x07aa: 0x321b, 0x07ab: 0x321b, 0x07ac: 0x321b, 0x07ad: 0x321b, 0x07ae: 0x321e, 0x07af: 0x321e, + 0x07b0: 0x06c4, 0x07b1: 0x06c4, // Block 0x1f, offset 0x7c0 - 0x07c0: 0x07a9, - 0x07c7: 0x07b0, 0x07c8: 0x07b7, 0x07ca: 0x07be, 0x07cb: 0x07c5, + 0x07d3: 0x3221, 0x07d4: 0x3221, 0x07d5: 0x3221, 0x07d6: 0x3221, 0x07d7: 0x3224, + 0x07d8: 0x3224, 0x07d9: 0x3227, 0x07da: 0x3227, 0x07db: 0x322a, 0x07dc: 0x322a, 0x07dd: 0x06b0, + 0x07de: 0x322d, 0x07df: 0x322d, 0x07e0: 0x3230, 0x07e1: 0x3230, 0x07e2: 0x3233, 0x07e3: 0x3233, + 0x07e4: 0x3236, 0x07e5: 0x3236, 0x07e6: 0x3236, 0x07e7: 0x3236, 0x07e8: 0x3239, 0x07e9: 0x3239, + 0x07ea: 0x323c, 0x07eb: 0x323c, 0x07ec: 0x3243, 0x07ed: 0x3243, 0x07ee: 0x324a, 0x07ef: 0x324a, + 0x07f0: 0x3251, 0x07f1: 0x3251, 0x07f2: 0x3258, 0x07f3: 0x3258, 0x07f4: 0x325f, 0x07f5: 0x325f, + 0x07f6: 0x3266, 0x07f7: 0x3266, 0x07f8: 0x3266, 0x07f9: 0x326d, 0x07fa: 0x326d, 0x07fb: 0x326d, + 0x07fc: 0x3274, 0x07fd: 0x3274, 0x07fe: 0x3274, 0x07ff: 0x3274, // Block 0x20, offset 0x800 - 0x080a: 0x07cf, 0x080b: 0x07d6, - 0x080c: 0x07dd, + 0x0800: 0x33ba, 0x0801: 0x33bf, 0x0802: 0x33c4, 0x0803: 0x33c9, 0x0804: 0x33ce, 0x0805: 0x33d3, + 0x0806: 0x33d8, 0x0807: 0x33dd, 0x0808: 0x33e2, 0x0809: 0x33e7, 0x080a: 0x33ec, 0x080b: 0x33f1, + 0x080c: 0x33f6, 0x080d: 0x33fb, 0x080e: 0x3400, 0x080f: 0x3405, 0x0810: 0x340a, 0x0811: 0x340f, + 0x0812: 0x3414, 0x0813: 0x3419, 0x0814: 0x341e, 0x0815: 0x3423, 0x0816: 0x3428, 0x0817: 0x342d, + 0x0818: 0x3432, 0x0819: 0x3437, 0x081a: 0x343c, 0x081b: 0x3441, 0x081c: 0x3446, 0x081d: 0x344b, + 0x081e: 0x3450, 0x081f: 0x3456, 0x0820: 0x345c, 0x0821: 0x3462, 0x0822: 0x3468, 0x0823: 0x346e, + 0x0824: 0x3474, 0x0825: 0x347b, 0x0826: 0x3285, 0x0827: 0x3482, 0x0828: 0x326d, 0x0829: 0x328c, + 0x082a: 0x3489, 0x082b: 0x348e, 0x082c: 0x32a2, 0x082d: 0x3493, 0x082e: 0x32a7, 0x082f: 0x32ac, + 0x0830: 0x3498, 0x0831: 0x349d, 0x0832: 0x32c0, 0x0833: 0x34a2, 0x0834: 0x32c5, 0x0835: 0x32ca, + 0x0836: 0x34a7, 0x0837: 0x34ac, 0x0838: 0x32d4, 0x0839: 0x34b1, 0x083a: 0x32d9, 0x083b: 0x32de, + 0x083c: 0x336f, 0x083d: 0x3374, 0x083e: 0x3383, 0x083f: 0x3388, // Block 0x21, offset 0x840 - 0x085a: 0x07e4, 0x085c: 0x07eb, 0x085d: 0x07f2, - 0x085e: 0x07fc, + 0x0840: 0x338d, 0x0841: 0x33a1, 0x0842: 0x33a6, 0x0843: 0x33ab, 0x0844: 0x33b0, 0x0845: 0x33c4, + 0x0846: 0x33c9, 0x0847: 0x33ce, 0x0848: 0x34b6, 0x0849: 0x33e2, 0x084a: 0x34bb, 0x084b: 0x34c0, + 0x084c: 0x3400, 0x084d: 0x34c5, 0x084e: 0x3405, 0x084f: 0x340a, 0x0850: 0x344b, 0x0851: 0x34ca, + 0x0852: 0x34cf, 0x0853: 0x3432, 0x0854: 0x34d4, 0x0855: 0x3437, 0x0856: 0x343c, 0x0857: 0x3277, + 0x0858: 0x327e, 0x0859: 0x34d9, 0x085a: 0x3285, 0x085b: 0x34e0, 0x085c: 0x3293, 0x085d: 0x3298, + 0x085e: 0x329d, 0x085f: 0x32a2, 0x0860: 0x34e7, 0x0861: 0x32b1, 0x0862: 0x32b6, 0x0863: 0x32bb, + 0x0864: 0x32c0, 0x0865: 0x34ec, 0x0866: 0x32d4, 0x0867: 0x32e3, 0x0868: 0x32e8, 0x0869: 0x32ed, + 0x086a: 0x32f2, 0x086b: 0x32f7, 0x086c: 0x3301, 0x086d: 0x3306, 0x086e: 0x330b, 0x086f: 0x3310, + 0x0870: 0x3315, 0x0871: 0x331a, 0x0872: 0x34f1, 0x0873: 0x331f, 0x0874: 0x3324, 0x0875: 0x3329, + 0x0876: 0x332e, 0x0877: 0x3333, 0x0878: 0x3338, 0x0879: 0x3342, 0x087a: 0x3347, 0x087b: 0x334c, + 0x087c: 0x3351, 0x087d: 0x3356, 0x087e: 0x335b, 0x087f: 0x3360, // Block 0x22, offset 0x880 - 0x08b3: 0x0803, + 0x0880: 0x3365, 0x0881: 0x336a, 0x0882: 0x3379, 0x0883: 0x337e, 0x0884: 0x3392, 0x0885: 0x3397, + 0x0886: 0x339c, 0x0887: 0x33a1, 0x0888: 0x33a6, 0x0889: 0x33b5, 0x088a: 0x33ba, 0x088b: 0x33bf, + 0x088c: 0x33c4, 0x088d: 0x34f6, 0x088e: 0x33d3, 0x088f: 0x33d8, 0x0890: 0x33dd, 0x0891: 0x33e2, + 0x0892: 0x33f1, 0x0893: 0x33f6, 0x0894: 0x33fb, 0x0895: 0x3400, 0x0896: 0x34fb, 0x0897: 0x340f, + 0x0898: 0x3414, 0x0899: 0x3500, 0x089a: 0x3423, 0x089b: 0x3428, 0x089c: 0x342d, 0x089d: 0x3432, + 0x089e: 0x3505, 0x089f: 0x3285, 0x08a0: 0x34e0, 0x08a1: 0x32a2, 0x08a2: 0x34e7, 0x08a3: 0x32c0, + 0x08a4: 0x34ec, 0x08a5: 0x32d4, 0x08a6: 0x350a, 0x08a7: 0x3315, 0x08a8: 0x350f, 0x08a9: 0x3514, + 0x08aa: 0x3519, 0x08ab: 0x33a1, 0x08ac: 0x33a6, 0x08ad: 0x33c4, 0x08ae: 0x3400, 0x08af: 0x34fb, + 0x08b0: 0x3432, 0x08b1: 0x3505, 0x08b2: 0x351e, 0x08b3: 0x3525, 0x08b4: 0x352c, 0x08b5: 0x3533, + 0x08b6: 0x3538, 0x08b7: 0x353d, 0x08b8: 0x3542, 0x08b9: 0x3547, 0x08ba: 0x354c, 0x08bb: 0x3551, + 0x08bc: 0x3556, 0x08bd: 0x355b, 0x08be: 0x3560, 0x08bf: 0x3565, // Block 0x23, offset 0x8c0 - 0x08f3: 0x080a, + 0x08c0: 0x16c7, 0x08c1: 0x391e, 0x08c2: 0x3922, 0x08c3: 0x3926, 0x08c4: 0x392a, + 0x08c7: 0x392e, 0x08c8: 0x3930, 0x08c9: 0x146c, 0x08ca: 0x146c, 0x08cb: 0x146c, + 0x08cc: 0x146c, 0x08cd: 0x3900, 0x08ce: 0x3900, 0x08cf: 0x3900, 0x08d0: 0x38e0, 0x08d1: 0x38e2, + 0x08d2: 0x143e, 0x08d4: 0x04e1, 0x08d5: 0x38ea, 0x08d6: 0x38ee, 0x08d7: 0x38ec, + 0x08d8: 0x38f8, 0x08d9: 0x149c, 0x08da: 0x149e, 0x08db: 0x3902, 0x08dc: 0x3904, 0x08dd: 0x3906, + 0x08de: 0x390a, 0x08df: 0x3932, 0x08e0: 0x3934, 0x08e1: 0x3936, 0x08e2: 0x1494, 0x08e3: 0x3938, + 0x08e4: 0x393a, 0x08e5: 0x393c, 0x08e6: 0x149a, 0x08e8: 0x393e, 0x08e9: 0x3940, + 0x08ea: 0x3942, 0x08eb: 0x3944, + 0x08f0: 0x3946, 0x08f1: 0x394a, 0x08f2: 0x394f, 0x08f4: 0x3953, + 0x08f6: 0x3957, 0x08f7: 0x395b, 0x08f8: 0x3960, 0x08f9: 0x3964, 0x08fa: 0x3969, 0x08fb: 0x396d, + 0x08fc: 0x3972, 0x08fd: 0x3976, 0x08fe: 0x397b, 0x08ff: 0x397f, // Block 0x24, offset 0x900 - 0x091c: 0x0811, 0x091d: 0x0818, + 0x0900: 0x3984, 0x0901: 0x068d, 0x0902: 0x068d, 0x0903: 0x0692, 0x0904: 0x0692, 0x0905: 0x0697, + 0x0906: 0x0697, 0x0907: 0x069c, 0x0908: 0x069c, 0x0909: 0x06a1, 0x090a: 0x06a1, 0x090b: 0x06a1, + 0x090c: 0x06a1, 0x090d: 0x3987, 0x090e: 0x3987, 0x090f: 0x398a, 0x0910: 0x398a, 0x0911: 0x398a, + 0x0912: 0x398a, 0x0913: 0x398d, 0x0914: 0x398d, 0x0915: 0x3990, 0x0916: 0x3990, 0x0917: 0x3990, + 0x0918: 0x3990, 0x0919: 0x3993, 0x091a: 0x3993, 0x091b: 0x3993, 0x091c: 0x3993, 0x091d: 0x3996, + 0x091e: 0x3996, 0x091f: 0x3996, 0x0920: 0x3996, 0x0921: 0x3999, 0x0922: 0x3999, 0x0923: 0x3999, + 0x0924: 0x3999, 0x0925: 0x399c, 0x0926: 0x399c, 0x0927: 0x399c, 0x0928: 0x399c, 0x0929: 0x399f, + 0x092a: 0x399f, 0x092b: 0x39a2, 0x092c: 0x39a2, 0x092d: 0x39a5, 0x092e: 0x39a5, 0x092f: 0x39a8, + 0x0930: 0x39a8, 0x0931: 0x39ab, 0x0932: 0x39ab, 0x0933: 0x39ab, 0x0934: 0x39ab, 0x0935: 0x39ae, + 0x0936: 0x39ae, 0x0937: 0x39ae, 0x0938: 0x39ae, 0x0939: 0x39b1, 0x093a: 0x39b1, 0x093b: 0x39b1, + 0x093c: 0x39b1, 0x093d: 0x39b4, 0x093e: 0x39b4, 0x093f: 0x39b4, // Block 0x25, offset 0x940 - 0x094c: 0x081f, + 0x0940: 0x39b4, 0x0941: 0x39b7, 0x0942: 0x39b7, 0x0943: 0x39b7, 0x0944: 0x39b7, 0x0945: 0x39ba, + 0x0946: 0x39ba, 0x0947: 0x39ba, 0x0948: 0x39ba, 0x0949: 0x39bd, 0x094a: 0x39bd, 0x094b: 0x39bd, + 0x094c: 0x39bd, 0x094d: 0x39c0, 0x094e: 0x39c0, 0x094f: 0x39c0, 0x0950: 0x39c0, 0x0951: 0x39c3, + 0x0952: 0x39c3, 0x0953: 0x39c3, 0x0954: 0x39c3, 0x0955: 0x39c6, 0x0956: 0x39c6, 0x0957: 0x39c6, + 0x0958: 0x39c6, 0x0959: 0x39c9, 0x095a: 0x39c9, 0x095b: 0x39c9, 0x095c: 0x39c9, 0x095d: 0x39cc, + 0x095e: 0x39cc, 0x095f: 0x39cc, 0x0960: 0x39cc, 0x0961: 0x39cf, 0x0962: 0x39cf, 0x0963: 0x39cf, + 0x0964: 0x39cf, 0x0965: 0x39d2, 0x0966: 0x39d2, 0x0967: 0x39d2, 0x0968: 0x39d2, 0x0969: 0x39d5, + 0x096a: 0x39d5, 0x096b: 0x39d5, 0x096c: 0x39d5, 0x096d: 0x39d8, 0x096e: 0x39d8, 0x096f: 0x3239, + 0x0970: 0x3239, 0x0971: 0x39db, 0x0972: 0x39db, 0x0973: 0x39db, 0x0974: 0x39db, 0x0975: 0x39de, + 0x0976: 0x39de, 0x0977: 0x39e5, 0x0978: 0x39e5, 0x0979: 0x39ec, 0x097a: 0x39ec, 0x097b: 0x39f3, + 0x097c: 0x39f3, // Block 0x26, offset 0x980 - 0x0983: 0x0823, - 0x098d: 0x082a, - 0x0992: 0x0831, 0x0997: 0x0838, - 0x099c: 0x083f, - 0x09a9: 0x0846, - 0x09b3: 0x084d, 0x09b5: 0x0854, - 0x09b6: 0x085b, 0x09b7: 0x0862, 0x09b8: 0x086c, 0x09b9: 0x0873, + 0x0981: 0x38ec, 0x0982: 0x39f8, 0x0983: 0x3932, 0x0984: 0x3940, 0x0985: 0x3942, + 0x0986: 0x3934, 0x0987: 0x39fa, 0x0988: 0x149c, 0x0989: 0x149e, 0x098a: 0x3936, 0x098b: 0x1494, + 0x098c: 0x38e0, 0x098d: 0x3938, 0x098e: 0x143e, 0x098f: 0x39fc, 0x0990: 0x1486, 0x0991: 0x001c, + 0x0992: 0x000d, 0x0993: 0x000f, 0x0994: 0x1488, 0x0995: 0x148a, 0x0996: 0x148c, 0x0997: 0x148e, + 0x0998: 0x1490, 0x0999: 0x1492, 0x099a: 0x38ea, 0x099b: 0x04e1, 0x099c: 0x393a, 0x099d: 0x149a, + 0x099e: 0x393c, 0x099f: 0x38ee, 0x09a0: 0x3944, 0x09a1: 0x0906, 0x09a2: 0x090b, 0x09a3: 0x14ad, + 0x09a4: 0x090d, 0x09a5: 0x090f, 0x09a6: 0x14d9, 0x09a7: 0x0914, 0x09a8: 0x0916, 0x09a9: 0x0918, + 0x09aa: 0x091a, 0x09ab: 0x091c, 0x09ac: 0x091e, 0x09ad: 0x0920, 0x09ae: 0x0922, 0x09af: 0x0924, + 0x09b0: 0x0929, 0x09b1: 0x14c8, 0x09b2: 0x092b, 0x09b3: 0x17f6, 0x09b4: 0x092d, 0x09b5: 0x092f, + 0x09b6: 0x155f, 0x09b7: 0x0931, 0x09b8: 0x1570, 0x09b9: 0x17f8, 0x09ba: 0x14d4, 0x09bb: 0x392e, + 0x09bc: 0x393e, 0x09bd: 0x3930, 0x09be: 0x39fe, 0x09bf: 0x3900, // Block 0x27, offset 0x9c0 - 0x09c1: 0x087d, - 0x09d3: 0x0884, - 0x09dd: 0x088b, - 0x09e2: 0x0892, - 0x09e7: 0x0899, - 0x09ec: 0x08a0, - 0x09f9: 0x08a7, + 0x09c0: 0x13f7, 0x09c1: 0x0007, 0x09c2: 0x093d, 0x09c3: 0x0984, 0x09c4: 0x093f, 0x09c5: 0x0941, + 0x09c6: 0x098c, 0x09c7: 0x094c, 0x09c8: 0x0494, 0x09c9: 0x097c, 0x09ca: 0x0499, 0x09cb: 0x094e, + 0x09cc: 0x04c5, 0x09cd: 0x0950, 0x09ce: 0x14a0, 0x09cf: 0x001e, 0x09d0: 0x0960, 0x09d1: 0x17fa, + 0x09d2: 0x049b, 0x09d3: 0x02c8, 0x09d4: 0x0962, 0x09d5: 0x0964, 0x09d6: 0x096d, 0x09d7: 0x04a6, + 0x09d8: 0x04c7, 0x09d9: 0x04a8, 0x09da: 0x09df, 0x09db: 0x3902, 0x09dc: 0x3a00, 0x09dd: 0x3904, + 0x09de: 0x3a02, 0x09df: 0x3a04, 0x09e0: 0x3a08, 0x09e1: 0x38e6, 0x09e2: 0x391e, 0x09e3: 0x3922, + 0x09e4: 0x38e2, 0x09e5: 0x3a0c, 0x09e6: 0x2321, 0x09e7: 0x3a10, 0x09e8: 0x3a14, 0x09e9: 0x3a18, + 0x09ea: 0x3a1c, 0x09eb: 0x3a20, 0x09ec: 0x3a24, 0x09ed: 0x3a28, 0x09ee: 0x3a2c, 0x09ef: 0x3a30, + 0x09f0: 0x3a34, 0x09f1: 0x2269, 0x09f2: 0x226d, 0x09f3: 0x2271, 0x09f4: 0x2275, 0x09f5: 0x2279, + 0x09f6: 0x227d, 0x09f7: 0x2281, 0x09f8: 0x2285, 0x09f9: 0x2289, 0x09fa: 0x228d, 0x09fb: 0x2291, + 0x09fc: 0x2295, 0x09fd: 0x2299, 0x09fe: 0x229d, 0x09ff: 0x22a1, // Block 0x28, offset 0xa00 - 0x0a26: 0x08ae, + 0x0a00: 0x0906, 0x0a01: 0x090b, 0x0a02: 0x14ad, 0x0a03: 0x090d, 0x0a04: 0x090f, 0x0a05: 0x14d9, + 0x0a06: 0x0914, 0x0a07: 0x0916, 0x0a08: 0x0918, 0x0a09: 0x091a, 0x0a0a: 0x091c, 0x0a0b: 0x091e, + 0x0a0c: 0x0920, 0x0a0d: 0x0922, 0x0a0e: 0x0924, 0x0a0f: 0x0929, 0x0a10: 0x14c8, 0x0a11: 0x092b, + 0x0a12: 0x17f6, 0x0a13: 0x092d, 0x0a14: 0x092f, 0x0a15: 0x155f, 0x0a16: 0x0931, 0x0a17: 0x1570, + 0x0a18: 0x17f8, 0x0a19: 0x14d4, 0x0a1a: 0x0007, 0x0a1b: 0x093d, 0x0a1c: 0x0984, 0x0a1d: 0x093f, + 0x0a1e: 0x0941, 0x0a1f: 0x098c, 0x0a20: 0x094c, 0x0a21: 0x0494, 0x0a22: 0x097c, 0x0a23: 0x0499, + 0x0a24: 0x094e, 0x0a25: 0x04c5, 0x0a26: 0x0950, 0x0a27: 0x14a0, 0x0a28: 0x001e, 0x0a29: 0x0960, + 0x0a2a: 0x17fa, 0x0a2b: 0x049b, 0x0a2c: 0x02c8, 0x0a2d: 0x0962, 0x0a2e: 0x0964, 0x0a2f: 0x096d, + 0x0a30: 0x04a6, 0x0a31: 0x04c7, 0x0a32: 0x04a8, 0x0a33: 0x09df, 0x0a34: 0x0906, 0x0a35: 0x090b, + 0x0a36: 0x14ad, 0x0a37: 0x090d, 0x0a38: 0x090f, 0x0a39: 0x14d9, 0x0a3a: 0x0914, 0x0a3b: 0x0916, + 0x0a3c: 0x0918, 0x0a3d: 0x091a, 0x0a3e: 0x091c, 0x0a3f: 0x091e, // Block 0x29, offset 0xa40 - 0x0a7c: 0x08b5, + 0x0a40: 0x0920, 0x0a41: 0x0922, 0x0a42: 0x0924, 0x0a43: 0x0929, 0x0a44: 0x14c8, 0x0a45: 0x092b, + 0x0a46: 0x17f6, 0x0a47: 0x092d, 0x0a48: 0x092f, 0x0a49: 0x155f, 0x0a4a: 0x0931, 0x0a4b: 0x1570, + 0x0a4c: 0x17f8, 0x0a4d: 0x14d4, 0x0a4e: 0x0007, 0x0a4f: 0x093d, 0x0a50: 0x0984, 0x0a51: 0x093f, + 0x0a52: 0x0941, 0x0a53: 0x098c, 0x0a54: 0x094c, 0x0a56: 0x097c, 0x0a57: 0x0499, + 0x0a58: 0x094e, 0x0a59: 0x04c5, 0x0a5a: 0x0950, 0x0a5b: 0x14a0, 0x0a5c: 0x001e, 0x0a5d: 0x0960, + 0x0a5e: 0x17fa, 0x0a5f: 0x049b, 0x0a60: 0x02c8, 0x0a61: 0x0962, 0x0a62: 0x0964, 0x0a63: 0x096d, + 0x0a64: 0x04a6, 0x0a65: 0x04c7, 0x0a66: 0x04a8, 0x0a67: 0x09df, 0x0a68: 0x0906, 0x0a69: 0x090b, + 0x0a6a: 0x14ad, 0x0a6b: 0x090d, 0x0a6c: 0x090f, 0x0a6d: 0x14d9, 0x0a6e: 0x0914, 0x0a6f: 0x0916, + 0x0a70: 0x0918, 0x0a71: 0x091a, 0x0a72: 0x091c, 0x0a73: 0x091e, 0x0a74: 0x0920, 0x0a75: 0x0922, + 0x0a76: 0x0924, 0x0a77: 0x0929, 0x0a78: 0x14c8, 0x0a79: 0x092b, 0x0a7a: 0x17f6, 0x0a7b: 0x092d, + 0x0a7c: 0x092f, 0x0a7d: 0x155f, 0x0a7e: 0x0931, 0x0a7f: 0x1570, // Block 0x2a, offset 0xa80 - 0x0a86: 0x08b9, 0x0a88: 0x08c0, 0x0a8a: 0x08c7, - 0x0a8c: 0x08ce, 0x0a8e: 0x08d5, - 0x0a92: 0x08dc, - 0x0abb: 0x08e3, - 0x0abd: 0x08ea, + 0x0a80: 0x17f8, 0x0a81: 0x14d4, 0x0a82: 0x0007, 0x0a83: 0x093d, 0x0a84: 0x0984, 0x0a85: 0x093f, + 0x0a86: 0x0941, 0x0a87: 0x098c, 0x0a88: 0x094c, 0x0a89: 0x0494, 0x0a8a: 0x097c, 0x0a8b: 0x0499, + 0x0a8c: 0x094e, 0x0a8d: 0x04c5, 0x0a8e: 0x0950, 0x0a8f: 0x14a0, 0x0a90: 0x001e, 0x0a91: 0x0960, + 0x0a92: 0x17fa, 0x0a93: 0x049b, 0x0a94: 0x02c8, 0x0a95: 0x0962, 0x0a96: 0x0964, 0x0a97: 0x096d, + 0x0a98: 0x04a6, 0x0a99: 0x04c7, 0x0a9a: 0x04a8, 0x0a9b: 0x09df, 0x0a9c: 0x0906, + 0x0a9e: 0x14ad, 0x0a9f: 0x090d, 0x0aa2: 0x0914, + 0x0aa5: 0x091a, 0x0aa6: 0x091c, 0x0aa9: 0x0922, + 0x0aaa: 0x0924, 0x0aab: 0x0929, 0x0aac: 0x14c8, 0x0aae: 0x17f6, 0x0aaf: 0x092d, + 0x0ab0: 0x092f, 0x0ab1: 0x155f, 0x0ab2: 0x0931, 0x0ab3: 0x1570, 0x0ab4: 0x17f8, 0x0ab5: 0x14d4, + 0x0ab6: 0x0007, 0x0ab7: 0x093d, 0x0ab8: 0x0984, 0x0ab9: 0x093f, 0x0abb: 0x098c, + 0x0abd: 0x0494, 0x0abe: 0x097c, 0x0abf: 0x0499, // Block 0x2b, offset 0xac0 - 0x0ac0: 0x08f1, 0x0ac1: 0x08f8, 0x0ac3: 0x08ff, + 0x0ac0: 0x094e, 0x0ac1: 0x04c5, 0x0ac2: 0x0950, 0x0ac3: 0x14a0, 0x0ac5: 0x0960, + 0x0ac6: 0x17fa, 0x0ac7: 0x049b, 0x0ac8: 0x02c8, 0x0ac9: 0x0962, 0x0aca: 0x0964, 0x0acb: 0x096d, + 0x0acc: 0x04a6, 0x0acd: 0x04c7, 0x0ace: 0x04a8, 0x0acf: 0x09df, 0x0ad0: 0x0906, 0x0ad1: 0x090b, + 0x0ad2: 0x14ad, 0x0ad3: 0x090d, 0x0ad4: 0x090f, 0x0ad5: 0x14d9, 0x0ad6: 0x0914, 0x0ad7: 0x0916, + 0x0ad8: 0x0918, 0x0ad9: 0x091a, 0x0ada: 0x091c, 0x0adb: 0x091e, 0x0adc: 0x0920, 0x0add: 0x0922, + 0x0ade: 0x0924, 0x0adf: 0x0929, 0x0ae0: 0x14c8, 0x0ae1: 0x092b, 0x0ae2: 0x17f6, 0x0ae3: 0x092d, + 0x0ae4: 0x092f, 0x0ae5: 0x155f, 0x0ae6: 0x0931, 0x0ae7: 0x1570, 0x0ae8: 0x17f8, 0x0ae9: 0x14d4, + 0x0aea: 0x0007, 0x0aeb: 0x093d, 0x0aec: 0x0984, 0x0aed: 0x093f, 0x0aee: 0x0941, 0x0aef: 0x098c, + 0x0af0: 0x094c, 0x0af1: 0x0494, 0x0af2: 0x097c, 0x0af3: 0x0499, 0x0af4: 0x094e, 0x0af5: 0x04c5, + 0x0af6: 0x0950, 0x0af7: 0x14a0, 0x0af8: 0x001e, 0x0af9: 0x0960, 0x0afa: 0x17fa, 0x0afb: 0x049b, + 0x0afc: 0x02c8, 0x0afd: 0x0962, 0x0afe: 0x0964, 0x0aff: 0x096d, // Block 0x2c, offset 0xb00 - 0x0b2c: 0x0906, 0x0b2d: 0x0908, 0x0b2e: 0x090b, - 0x0b30: 0x090d, 0x0b31: 0x090f, 0x0b32: 0x0911, 0x0b33: 0x0914, 0x0b34: 0x0916, 0x0b35: 0x0918, - 0x0b36: 0x091a, 0x0b37: 0x091c, 0x0b38: 0x091e, 0x0b39: 0x0920, 0x0b3a: 0x0922, - 0x0b3c: 0x0924, 0x0b3d: 0x0926, 0x0b3e: 0x0929, 0x0b3f: 0x092b, + 0x0b00: 0x04a6, 0x0b01: 0x04c7, 0x0b02: 0x04a8, 0x0b03: 0x09df, 0x0b04: 0x0906, 0x0b05: 0x090b, + 0x0b07: 0x090d, 0x0b08: 0x090f, 0x0b09: 0x14d9, 0x0b0a: 0x0914, + 0x0b0d: 0x091a, 0x0b0e: 0x091c, 0x0b0f: 0x091e, 0x0b10: 0x0920, 0x0b11: 0x0922, + 0x0b12: 0x0924, 0x0b13: 0x0929, 0x0b14: 0x14c8, 0x0b16: 0x17f6, 0x0b17: 0x092d, + 0x0b18: 0x092f, 0x0b19: 0x155f, 0x0b1a: 0x0931, 0x0b1b: 0x1570, 0x0b1c: 0x17f8, + 0x0b1e: 0x0007, 0x0b1f: 0x093d, 0x0b20: 0x0984, 0x0b21: 0x093f, 0x0b22: 0x0941, 0x0b23: 0x098c, + 0x0b24: 0x094c, 0x0b25: 0x0494, 0x0b26: 0x097c, 0x0b27: 0x0499, 0x0b28: 0x094e, 0x0b29: 0x04c5, + 0x0b2a: 0x0950, 0x0b2b: 0x14a0, 0x0b2c: 0x001e, 0x0b2d: 0x0960, 0x0b2e: 0x17fa, 0x0b2f: 0x049b, + 0x0b30: 0x02c8, 0x0b31: 0x0962, 0x0b32: 0x0964, 0x0b33: 0x096d, 0x0b34: 0x04a6, 0x0b35: 0x04c7, + 0x0b36: 0x04a8, 0x0b37: 0x09df, 0x0b38: 0x0906, 0x0b39: 0x090b, 0x0b3b: 0x090d, + 0x0b3c: 0x090f, 0x0b3d: 0x14d9, 0x0b3e: 0x0914, // Block 0x2d, offset 0xb40 - 0x0b40: 0x092d, 0x0b41: 0x092f, 0x0b42: 0x0931, 0x0b43: 0x0007, 0x0b44: 0x0933, 0x0b45: 0x0936, - 0x0b46: 0x0939, 0x0b47: 0x093d, 0x0b48: 0x093f, 0x0b49: 0x0941, 0x0b4a: 0x0943, 0x0b4b: 0x0946, - 0x0b4c: 0x0949, 0x0b4d: 0x094c, 0x0b4f: 0x094e, 0x0b50: 0x0950, 0x0b51: 0x0952, - 0x0b52: 0x001e, 0x0b53: 0x0955, 0x0b54: 0x0958, 0x0b55: 0x095c, 0x0b56: 0x0960, 0x0b57: 0x0962, - 0x0b58: 0x0964, 0x0b59: 0x0966, 0x0b5a: 0x096a, 0x0b5b: 0x096d, 0x0b5c: 0x096f, 0x0b5d: 0x0559, - 0x0b5e: 0x0973, 0x0b5f: 0x0976, 0x0b60: 0x056c, 0x0b61: 0x0979, 0x0b62: 0x097c, 0x0b63: 0x049b, - 0x0b64: 0x0964, 0x0b65: 0x096d, 0x0b66: 0x0559, 0x0b67: 0x0973, 0x0b68: 0x0575, 0x0b69: 0x056c, - 0x0b6a: 0x0979, - 0x0b78: 0x097e, + 0x0b40: 0x0918, 0x0b41: 0x091a, 0x0b42: 0x091c, 0x0b43: 0x091e, 0x0b44: 0x0920, + 0x0b46: 0x0924, 0x0b4a: 0x17f6, 0x0b4b: 0x092d, + 0x0b4c: 0x092f, 0x0b4d: 0x155f, 0x0b4e: 0x0931, 0x0b4f: 0x1570, 0x0b50: 0x17f8, + 0x0b52: 0x0007, 0x0b53: 0x093d, 0x0b54: 0x0984, 0x0b55: 0x093f, 0x0b56: 0x0941, 0x0b57: 0x098c, + 0x0b58: 0x094c, 0x0b59: 0x0494, 0x0b5a: 0x097c, 0x0b5b: 0x0499, 0x0b5c: 0x094e, 0x0b5d: 0x04c5, + 0x0b5e: 0x0950, 0x0b5f: 0x14a0, 0x0b60: 0x001e, 0x0b61: 0x0960, 0x0b62: 0x17fa, 0x0b63: 0x049b, + 0x0b64: 0x02c8, 0x0b65: 0x0962, 0x0b66: 0x0964, 0x0b67: 0x096d, 0x0b68: 0x04a6, 0x0b69: 0x04c7, + 0x0b6a: 0x04a8, 0x0b6b: 0x09df, 0x0b6c: 0x0906, 0x0b6d: 0x090b, 0x0b6e: 0x14ad, 0x0b6f: 0x090d, + 0x0b70: 0x090f, 0x0b71: 0x14d9, 0x0b72: 0x0914, 0x0b73: 0x0916, 0x0b74: 0x0918, 0x0b75: 0x091a, + 0x0b76: 0x091c, 0x0b77: 0x091e, 0x0b78: 0x0920, 0x0b79: 0x0922, 0x0b7a: 0x0924, 0x0b7b: 0x0929, + 0x0b7c: 0x14c8, 0x0b7d: 0x092b, 0x0b7e: 0x17f6, 0x0b7f: 0x092d, // Block 0x2e, offset 0xb80 - 0x0b9b: 0x0981, 0x0b9c: 0x0984, 0x0b9d: 0x0986, - 0x0b9e: 0x0989, 0x0b9f: 0x0949, 0x0ba0: 0x098c, 0x0ba1: 0x098e, 0x0ba2: 0x0991, 0x0ba3: 0x0994, - 0x0ba4: 0x0997, 0x0ba5: 0x099a, 0x0ba6: 0x099d, 0x0ba7: 0x09a0, 0x0ba8: 0x09a4, 0x0ba9: 0x09a7, - 0x0baa: 0x09aa, 0x0bab: 0x09ae, 0x0bac: 0x09b1, 0x0bad: 0x09b4, 0x0bae: 0x09b7, 0x0baf: 0x09ba, - 0x0bb0: 0x09bd, 0x0bb1: 0x09c0, 0x0bb2: 0x09c3, 0x0bb3: 0x09c6, 0x0bb4: 0x09c9, 0x0bb5: 0x09cc, - 0x0bb6: 0x09cf, 0x0bb7: 0x09d2, 0x0bb8: 0x09d5, 0x0bb9: 0x09d9, 0x0bba: 0x09dc, 0x0bbb: 0x09df, - 0x0bbc: 0x09e1, 0x0bbd: 0x09e4, 0x0bbe: 0x09e7, 0x0bbf: 0x055c, + 0x0b80: 0x092f, 0x0b81: 0x155f, 0x0b82: 0x0931, 0x0b83: 0x1570, 0x0b84: 0x17f8, 0x0b85: 0x14d4, + 0x0b86: 0x0007, 0x0b87: 0x093d, 0x0b88: 0x0984, 0x0b89: 0x093f, 0x0b8a: 0x0941, 0x0b8b: 0x098c, + 0x0b8c: 0x094c, 0x0b8d: 0x0494, 0x0b8e: 0x097c, 0x0b8f: 0x0499, 0x0b90: 0x094e, 0x0b91: 0x04c5, + 0x0b92: 0x0950, 0x0b93: 0x14a0, 0x0b94: 0x001e, 0x0b95: 0x0960, 0x0b96: 0x17fa, 0x0b97: 0x049b, + 0x0b98: 0x02c8, 0x0b99: 0x0962, 0x0b9a: 0x0964, 0x0b9b: 0x096d, 0x0b9c: 0x04a6, 0x0b9d: 0x04c7, + 0x0b9e: 0x04a8, 0x0b9f: 0x09df, 0x0ba0: 0x0906, 0x0ba1: 0x090b, 0x0ba2: 0x14ad, 0x0ba3: 0x090d, + 0x0ba4: 0x090f, 0x0ba5: 0x14d9, 0x0ba6: 0x0914, 0x0ba7: 0x0916, 0x0ba8: 0x0918, 0x0ba9: 0x091a, + 0x0baa: 0x091c, 0x0bab: 0x091e, 0x0bac: 0x0920, 0x0bad: 0x0922, 0x0bae: 0x0924, 0x0baf: 0x0929, + 0x0bb0: 0x14c8, 0x0bb1: 0x092b, 0x0bb2: 0x17f6, 0x0bb3: 0x092d, 0x0bb4: 0x092f, 0x0bb5: 0x155f, + 0x0bb6: 0x0931, 0x0bb7: 0x1570, 0x0bb8: 0x17f8, 0x0bb9: 0x14d4, 0x0bba: 0x0007, 0x0bbb: 0x093d, + 0x0bbc: 0x0984, 0x0bbd: 0x093f, 0x0bbe: 0x0941, 0x0bbf: 0x098c, // Block 0x2f, offset 0xbc0 - 0x0bc0: 0x09ea, 0x0bc1: 0x09ee, 0x0bc2: 0x09f2, 0x0bc3: 0x09f6, 0x0bc4: 0x09fa, 0x0bc5: 0x09fe, - 0x0bc6: 0x0a02, 0x0bc7: 0x0a06, 0x0bc8: 0x0a0a, 0x0bc9: 0x0a10, 0x0bca: 0x0a16, 0x0bcb: 0x0a1a, - 0x0bcc: 0x0a1e, 0x0bcd: 0x0a22, 0x0bce: 0x0a26, 0x0bcf: 0x0a2a, 0x0bd0: 0x0a2e, 0x0bd1: 0x0a32, - 0x0bd2: 0x0a36, 0x0bd3: 0x0a3a, 0x0bd4: 0x0a3e, 0x0bd5: 0x0a44, 0x0bd6: 0x0a4a, 0x0bd7: 0x0a50, - 0x0bd8: 0x0a56, 0x0bd9: 0x0a5a, 0x0bda: 0x0a5e, 0x0bdb: 0x0a62, 0x0bdc: 0x0a66, 0x0bdd: 0x0a6c, - 0x0bde: 0x0a72, 0x0bdf: 0x0a76, 0x0be0: 0x0a7a, 0x0be1: 0x0a7e, 0x0be2: 0x0a82, 0x0be3: 0x0a86, - 0x0be4: 0x0a8a, 0x0be5: 0x0a8e, 0x0be6: 0x0a92, 0x0be7: 0x0a96, 0x0be8: 0x0a9a, 0x0be9: 0x0a9e, - 0x0bea: 0x0aa2, 0x0beb: 0x0aa6, 0x0bec: 0x0aaa, 0x0bed: 0x0aae, 0x0bee: 0x0ab2, 0x0bef: 0x0ab8, - 0x0bf0: 0x0abe, 0x0bf1: 0x0ac2, 0x0bf2: 0x0ac6, 0x0bf3: 0x0aca, 0x0bf4: 0x0ace, 0x0bf5: 0x0ad2, - 0x0bf6: 0x0ad6, 0x0bf7: 0x0ada, 0x0bf8: 0x0ade, 0x0bf9: 0x0ae4, 0x0bfa: 0x0aea, 0x0bfb: 0x0aee, - 0x0bfc: 0x0af2, 0x0bfd: 0x0af6, 0x0bfe: 0x0afa, 0x0bff: 0x0afe, + 0x0bc0: 0x094c, 0x0bc1: 0x0494, 0x0bc2: 0x097c, 0x0bc3: 0x0499, 0x0bc4: 0x094e, 0x0bc5: 0x04c5, + 0x0bc6: 0x0950, 0x0bc7: 0x14a0, 0x0bc8: 0x001e, 0x0bc9: 0x0960, 0x0bca: 0x17fa, 0x0bcb: 0x049b, + 0x0bcc: 0x02c8, 0x0bcd: 0x0962, 0x0bce: 0x0964, 0x0bcf: 0x096d, 0x0bd0: 0x04a6, 0x0bd1: 0x04c7, + 0x0bd2: 0x04a8, 0x0bd3: 0x09df, 0x0bd4: 0x0906, 0x0bd5: 0x090b, 0x0bd6: 0x14ad, 0x0bd7: 0x090d, + 0x0bd8: 0x090f, 0x0bd9: 0x14d9, 0x0bda: 0x0914, 0x0bdb: 0x0916, 0x0bdc: 0x0918, 0x0bdd: 0x091a, + 0x0bde: 0x091c, 0x0bdf: 0x091e, 0x0be0: 0x0920, 0x0be1: 0x0922, 0x0be2: 0x0924, 0x0be3: 0x0929, + 0x0be4: 0x14c8, 0x0be5: 0x092b, 0x0be6: 0x17f6, 0x0be7: 0x092d, 0x0be8: 0x092f, 0x0be9: 0x155f, + 0x0bea: 0x0931, 0x0beb: 0x1570, 0x0bec: 0x17f8, 0x0bed: 0x14d4, 0x0bee: 0x0007, 0x0bef: 0x093d, + 0x0bf0: 0x0984, 0x0bf1: 0x093f, 0x0bf2: 0x0941, 0x0bf3: 0x098c, 0x0bf4: 0x094c, 0x0bf5: 0x0494, + 0x0bf6: 0x097c, 0x0bf7: 0x0499, 0x0bf8: 0x094e, 0x0bf9: 0x04c5, 0x0bfa: 0x0950, 0x0bfb: 0x14a0, + 0x0bfc: 0x001e, 0x0bfd: 0x0960, 0x0bfe: 0x17fa, 0x0bff: 0x049b, // Block 0x30, offset 0xc00 - 0x0c00: 0x0b02, 0x0c01: 0x0b06, 0x0c02: 0x0b0a, 0x0c03: 0x0b0e, 0x0c04: 0x0b12, 0x0c05: 0x0b16, - 0x0c06: 0x0b1a, 0x0c07: 0x0b1e, 0x0c08: 0x0b22, 0x0c09: 0x0b26, 0x0c0a: 0x0b2a, 0x0c0b: 0x0b2e, - 0x0c0c: 0x0b32, 0x0c0d: 0x0b38, 0x0c0e: 0x0b3e, 0x0c0f: 0x0b44, 0x0c10: 0x0b4a, 0x0c11: 0x0b50, - 0x0c12: 0x0b56, 0x0c13: 0x0b5c, 0x0c14: 0x0b62, 0x0c15: 0x0b66, 0x0c16: 0x0b6a, 0x0c17: 0x0b6e, - 0x0c18: 0x0b72, 0x0c19: 0x0b76, 0x0c1a: 0x0b7a, 0x0c1b: 0x0b7e, 0x0c1c: 0x0b82, 0x0c1d: 0x0b88, - 0x0c1e: 0x0b8e, 0x0c1f: 0x0b92, 0x0c20: 0x0b96, 0x0c21: 0x0b9a, 0x0c22: 0x0b9e, 0x0c23: 0x0ba2, - 0x0c24: 0x0ba6, 0x0c25: 0x0bac, 0x0c26: 0x0bb2, 0x0c27: 0x0bb8, 0x0c28: 0x0bbe, 0x0c29: 0x0bc4, - 0x0c2a: 0x0bca, 0x0c2b: 0x0bce, 0x0c2c: 0x0bd2, 0x0c2d: 0x0bd6, 0x0c2e: 0x0bda, 0x0c2f: 0x0bde, - 0x0c30: 0x0be2, 0x0c31: 0x0be6, 0x0c32: 0x0bea, 0x0c33: 0x0bee, 0x0c34: 0x0bf2, 0x0c35: 0x0bf6, - 0x0c36: 0x0bfa, 0x0c37: 0x0bfe, 0x0c38: 0x0c02, 0x0c39: 0x0c08, 0x0c3a: 0x0c0e, 0x0c3b: 0x0c14, - 0x0c3c: 0x0c1a, 0x0c3d: 0x0c1e, 0x0c3e: 0x0c22, 0x0c3f: 0x0c26, + 0x0c00: 0x02c8, 0x0c01: 0x0962, 0x0c02: 0x0964, 0x0c03: 0x096d, 0x0c04: 0x04a6, 0x0c05: 0x04c7, + 0x0c06: 0x04a8, 0x0c07: 0x09df, 0x0c08: 0x0906, 0x0c09: 0x090b, 0x0c0a: 0x14ad, 0x0c0b: 0x090d, + 0x0c0c: 0x090f, 0x0c0d: 0x14d9, 0x0c0e: 0x0914, 0x0c0f: 0x0916, 0x0c10: 0x0918, 0x0c11: 0x091a, + 0x0c12: 0x091c, 0x0c13: 0x091e, 0x0c14: 0x0920, 0x0c15: 0x0922, 0x0c16: 0x0924, 0x0c17: 0x0929, + 0x0c18: 0x14c8, 0x0c19: 0x092b, 0x0c1a: 0x17f6, 0x0c1b: 0x092d, 0x0c1c: 0x092f, 0x0c1d: 0x155f, + 0x0c1e: 0x0931, 0x0c1f: 0x1570, 0x0c20: 0x17f8, 0x0c21: 0x14d4, 0x0c22: 0x0007, 0x0c23: 0x093d, + 0x0c24: 0x0984, 0x0c25: 0x093f, 0x0c26: 0x0941, 0x0c27: 0x098c, 0x0c28: 0x094c, 0x0c29: 0x0494, + 0x0c2a: 0x097c, 0x0c2b: 0x0499, 0x0c2c: 0x094e, 0x0c2d: 0x04c5, 0x0c2e: 0x0950, 0x0c2f: 0x14a0, + 0x0c30: 0x001e, 0x0c31: 0x0960, 0x0c32: 0x17fa, 0x0c33: 0x049b, 0x0c34: 0x02c8, 0x0c35: 0x0962, + 0x0c36: 0x0964, 0x0c37: 0x096d, 0x0c38: 0x04a6, 0x0c39: 0x04c7, 0x0c3a: 0x04a8, 0x0c3b: 0x09df, + 0x0c3c: 0x0906, 0x0c3d: 0x090b, 0x0c3e: 0x14ad, 0x0c3f: 0x090d, // Block 0x31, offset 0xc40 - 0x0c40: 0x0c2a, 0x0c41: 0x0c2e, 0x0c42: 0x0c32, 0x0c43: 0x0c36, 0x0c44: 0x0c3a, 0x0c45: 0x0c3e, - 0x0c46: 0x0c42, 0x0c47: 0x0c46, 0x0c48: 0x0c4a, 0x0c49: 0x0c4e, 0x0c4a: 0x0c52, 0x0c4b: 0x0c56, - 0x0c4c: 0x0c5a, 0x0c4d: 0x0c5e, 0x0c4e: 0x0c62, 0x0c4f: 0x0c66, 0x0c50: 0x0c6a, 0x0c51: 0x0c6e, - 0x0c52: 0x0c72, 0x0c53: 0x0c76, 0x0c54: 0x0c7a, 0x0c55: 0x0c7e, 0x0c56: 0x0c82, 0x0c57: 0x0c86, - 0x0c58: 0x0c8a, 0x0c59: 0x0c8e, 0x0c5a: 0x0c92, 0x0c5b: 0x0b9a, - 0x0c60: 0x0c9b, 0x0c61: 0x0c9f, 0x0c62: 0x0ca3, 0x0c63: 0x0ca7, - 0x0c64: 0x0cab, 0x0c65: 0x0cb1, 0x0c66: 0x0cb7, 0x0c67: 0x0cbd, 0x0c68: 0x0cc3, 0x0c69: 0x0cc9, - 0x0c6a: 0x0ccf, 0x0c6b: 0x0cd5, 0x0c6c: 0x0cdb, 0x0c6d: 0x0ce1, 0x0c6e: 0x0ce7, 0x0c6f: 0x0ced, - 0x0c70: 0x0cf3, 0x0c71: 0x0cf9, 0x0c72: 0x0cff, 0x0c73: 0x0d05, 0x0c74: 0x0d0b, 0x0c75: 0x0d11, - 0x0c76: 0x0d17, 0x0c77: 0x0d1d, 0x0c78: 0x0d23, 0x0c79: 0x0d27, 0x0c7a: 0x0d2b, 0x0c7b: 0x0d2f, - 0x0c7c: 0x0d33, 0x0c7d: 0x0d37, 0x0c7e: 0x0d3b, 0x0c7f: 0x0d41, + 0x0c40: 0x090f, 0x0c41: 0x14d9, 0x0c42: 0x0914, 0x0c43: 0x0916, 0x0c44: 0x0918, 0x0c45: 0x091a, + 0x0c46: 0x091c, 0x0c47: 0x091e, 0x0c48: 0x0920, 0x0c49: 0x0922, 0x0c4a: 0x0924, 0x0c4b: 0x0929, + 0x0c4c: 0x14c8, 0x0c4d: 0x092b, 0x0c4e: 0x17f6, 0x0c4f: 0x092d, 0x0c50: 0x092f, 0x0c51: 0x155f, + 0x0c52: 0x0931, 0x0c53: 0x1570, 0x0c54: 0x17f8, 0x0c55: 0x14d4, 0x0c56: 0x0007, 0x0c57: 0x093d, + 0x0c58: 0x0984, 0x0c59: 0x093f, 0x0c5a: 0x0941, 0x0c5b: 0x098c, 0x0c5c: 0x094c, 0x0c5d: 0x0494, + 0x0c5e: 0x097c, 0x0c5f: 0x0499, 0x0c60: 0x094e, 0x0c61: 0x04c5, 0x0c62: 0x0950, 0x0c63: 0x14a0, + 0x0c64: 0x001e, 0x0c65: 0x0960, 0x0c66: 0x17fa, 0x0c67: 0x049b, 0x0c68: 0x02c8, 0x0c69: 0x0962, + 0x0c6a: 0x0964, 0x0c6b: 0x096d, 0x0c6c: 0x04a6, 0x0c6d: 0x04c7, 0x0c6e: 0x04a8, 0x0c6f: 0x09df, + 0x0c70: 0x0906, 0x0c71: 0x090b, 0x0c72: 0x14ad, 0x0c73: 0x090d, 0x0c74: 0x090f, 0x0c75: 0x14d9, + 0x0c76: 0x0914, 0x0c77: 0x0916, 0x0c78: 0x0918, 0x0c79: 0x091a, 0x0c7a: 0x091c, 0x0c7b: 0x091e, + 0x0c7c: 0x0920, 0x0c7d: 0x0922, 0x0c7e: 0x0924, 0x0c7f: 0x0929, // Block 0x32, offset 0xc80 - 0x0c80: 0x0d47, 0x0c81: 0x0d4d, 0x0c82: 0x0d53, 0x0c83: 0x0d59, 0x0c84: 0x0d5f, 0x0c85: 0x0d65, - 0x0c86: 0x0d6b, 0x0c87: 0x0d71, 0x0c88: 0x0d77, 0x0c89: 0x0d7b, 0x0c8a: 0x0d7f, 0x0c8b: 0x0d83, - 0x0c8c: 0x0d87, 0x0c8d: 0x0d8b, 0x0c8e: 0x0d8f, 0x0c8f: 0x0d93, 0x0c90: 0x0d97, 0x0c91: 0x0d9d, - 0x0c92: 0x0da3, 0x0c93: 0x0da9, 0x0c94: 0x0daf, 0x0c95: 0x0db5, 0x0c96: 0x0dbb, 0x0c97: 0x0dc1, - 0x0c98: 0x0dc7, 0x0c99: 0x0dcd, 0x0c9a: 0x0dd3, 0x0c9b: 0x0dd9, 0x0c9c: 0x0ddf, 0x0c9d: 0x0de5, - 0x0c9e: 0x0deb, 0x0c9f: 0x0df1, 0x0ca0: 0x0df7, 0x0ca1: 0x0dfd, 0x0ca2: 0x0e03, 0x0ca3: 0x0e09, - 0x0ca4: 0x0e0f, 0x0ca5: 0x0e13, 0x0ca6: 0x0e17, 0x0ca7: 0x0e1b, 0x0ca8: 0x0e1f, 0x0ca9: 0x0e25, - 0x0caa: 0x0e2b, 0x0cab: 0x0e31, 0x0cac: 0x0e37, 0x0cad: 0x0e3d, 0x0cae: 0x0e43, 0x0caf: 0x0e49, - 0x0cb0: 0x0e4f, 0x0cb1: 0x0e55, 0x0cb2: 0x0e5b, 0x0cb3: 0x0e5f, 0x0cb4: 0x0e63, 0x0cb5: 0x0e67, - 0x0cb6: 0x0e6b, 0x0cb7: 0x0e6f, 0x0cb8: 0x0e73, 0x0cb9: 0x0e77, + 0x0c80: 0x14c8, 0x0c81: 0x092b, 0x0c82: 0x17f6, 0x0c83: 0x092d, 0x0c84: 0x092f, 0x0c85: 0x155f, + 0x0c86: 0x0931, 0x0c87: 0x1570, 0x0c88: 0x17f8, 0x0c89: 0x14d4, 0x0c8a: 0x0007, 0x0c8b: 0x093d, + 0x0c8c: 0x0984, 0x0c8d: 0x093f, 0x0c8e: 0x0941, 0x0c8f: 0x098c, 0x0c90: 0x094c, 0x0c91: 0x0494, + 0x0c92: 0x097c, 0x0c93: 0x0499, 0x0c94: 0x094e, 0x0c95: 0x04c5, 0x0c96: 0x0950, 0x0c97: 0x14a0, + 0x0c98: 0x001e, 0x0c99: 0x0960, 0x0c9a: 0x17fa, 0x0c9b: 0x049b, 0x0c9c: 0x02c8, 0x0c9d: 0x0962, + 0x0c9e: 0x0964, 0x0c9f: 0x096d, 0x0ca0: 0x04a6, 0x0ca1: 0x04c7, 0x0ca2: 0x04a8, 0x0ca3: 0x09df, + 0x0ca4: 0x3b27, 0x0ca5: 0x3b2a, 0x0ca8: 0x3b2d, 0x0ca9: 0x3b30, + 0x0caa: 0x14eb, 0x0cab: 0x3b33, 0x0cac: 0x3b36, 0x0cad: 0x3b39, 0x0cae: 0x3b3c, 0x0caf: 0x057b, + 0x0cb0: 0x3b3f, 0x0cb1: 0x3b42, 0x0cb2: 0x3b45, 0x0cb3: 0x3b48, 0x0cb4: 0x3b4b, 0x0cb5: 0x3b4e, + 0x0cb6: 0x3b51, 0x0cb7: 0x14ee, 0x0cb8: 0x3b54, 0x0cb9: 0x057b, 0x0cba: 0x0581, 0x0cbb: 0x3b57, + 0x0cbc: 0x055f, 0x0cbd: 0x3b5a, 0x0cbe: 0x3b5d, 0x0cbf: 0x3b60, // Block 0x33, offset 0xcc0 - 0x0cc0: 0x0e7b, 0x0cc1: 0x0e80, 0x0cc2: 0x0e85, 0x0cc3: 0x0e8c, 0x0cc4: 0x0e93, 0x0cc5: 0x0e9a, - 0x0cc6: 0x0ea1, 0x0cc7: 0x0ea8, 0x0cc8: 0x0eaf, 0x0cc9: 0x0eb4, 0x0cca: 0x0eb9, 0x0ccb: 0x0ec0, - 0x0ccc: 0x0ec7, 0x0ccd: 0x0ece, 0x0cce: 0x0ed5, 0x0ccf: 0x0edc, 0x0cd0: 0x0ee3, 0x0cd1: 0x0ee8, - 0x0cd2: 0x0eed, 0x0cd3: 0x0ef4, 0x0cd4: 0x0efb, 0x0cd5: 0x0f02, - 0x0cd8: 0x0f09, 0x0cd9: 0x0f0e, 0x0cda: 0x0f13, 0x0cdb: 0x0f1a, 0x0cdc: 0x0f21, 0x0cdd: 0x0f28, - 0x0ce0: 0x0f2f, 0x0ce1: 0x0f34, 0x0ce2: 0x0f39, 0x0ce3: 0x0f40, - 0x0ce4: 0x0f47, 0x0ce5: 0x0f4e, 0x0ce6: 0x0f55, 0x0ce7: 0x0f5c, 0x0ce8: 0x0f63, 0x0ce9: 0x0f68, - 0x0cea: 0x0f6d, 0x0ceb: 0x0f74, 0x0cec: 0x0f7b, 0x0ced: 0x0f82, 0x0cee: 0x0f89, 0x0cef: 0x0f90, - 0x0cf0: 0x0f97, 0x0cf1: 0x0f9c, 0x0cf2: 0x0fa1, 0x0cf3: 0x0fa8, 0x0cf4: 0x0faf, 0x0cf5: 0x0fb6, - 0x0cf6: 0x0fbd, 0x0cf7: 0x0fc4, 0x0cf8: 0x0fcb, 0x0cf9: 0x0fd0, 0x0cfa: 0x0fd5, 0x0cfb: 0x0fdc, - 0x0cfc: 0x0fe3, 0x0cfd: 0x0fea, 0x0cfe: 0x0ff1, 0x0cff: 0x0ff8, + 0x0cc0: 0x14d6, 0x0cc1: 0x3b63, 0x0cc2: 0x3b67, 0x0cc3: 0x0559, 0x0cc4: 0x0973, 0x0cc5: 0x0976, + 0x0cc6: 0x057e, 0x0cc7: 0x3b6a, 0x0cc8: 0x3b6d, 0x0cc9: 0x055c, 0x0cca: 0x12fd, 0x0ccb: 0x0572, + 0x0ccc: 0x3b70, 0x0ccd: 0x0015, 0x0cce: 0x3b73, 0x0ccf: 0x3b76, 0x0cd0: 0x3b79, 0x0cd1: 0x056f, + 0x0cd2: 0x0575, 0x0cd3: 0x0578, 0x0cd4: 0x3b7c, 0x0cd5: 0x3b7f, 0x0cd6: 0x3b82, 0x0cd7: 0x056c, + 0x0cd8: 0x0979, 0x0cd9: 0x3b85, 0x0cda: 0x3b88, 0x0cdb: 0x3b8b, 0x0cdc: 0x057e, 0x0cdd: 0x055c, + 0x0cde: 0x0572, 0x0cdf: 0x056c, 0x0ce0: 0x0575, 0x0ce1: 0x056f, 0x0ce2: 0x3b2d, 0x0ce3: 0x3b30, + 0x0ce4: 0x14eb, 0x0ce5: 0x3b33, 0x0ce6: 0x3b36, 0x0ce7: 0x3b39, 0x0ce8: 0x3b3c, 0x0ce9: 0x057b, + 0x0cea: 0x3b3f, 0x0ceb: 0x3b42, 0x0cec: 0x3b45, 0x0ced: 0x3b48, 0x0cee: 0x3b4b, 0x0cef: 0x3b4e, + 0x0cf0: 0x3b51, 0x0cf1: 0x14ee, 0x0cf2: 0x3b54, 0x0cf3: 0x057b, 0x0cf4: 0x0581, 0x0cf5: 0x3b57, + 0x0cf6: 0x055f, 0x0cf7: 0x3b5a, 0x0cf8: 0x3b5d, 0x0cf9: 0x3b60, 0x0cfa: 0x14d6, 0x0cfb: 0x3b63, + 0x0cfc: 0x3b67, 0x0cfd: 0x0559, 0x0cfe: 0x0973, 0x0cff: 0x0976, // Block 0x34, offset 0xd00 - 0x0d00: 0x0fff, 0x0d01: 0x1004, 0x0d02: 0x1009, 0x0d03: 0x1010, 0x0d04: 0x1017, 0x0d05: 0x101e, - 0x0d08: 0x1025, 0x0d09: 0x102a, 0x0d0a: 0x102f, 0x0d0b: 0x1036, - 0x0d0c: 0x103d, 0x0d0d: 0x1044, 0x0d10: 0x104b, 0x0d11: 0x1050, - 0x0d12: 0x1055, 0x0d13: 0x105c, 0x0d14: 0x1063, 0x0d15: 0x106a, 0x0d16: 0x1071, 0x0d17: 0x1078, - 0x0d19: 0x107f, 0x0d1b: 0x1084, 0x0d1d: 0x108b, - 0x0d1f: 0x1092, 0x0d20: 0x1099, 0x0d21: 0x109e, 0x0d22: 0x10a3, 0x0d23: 0x10aa, - 0x0d24: 0x10b1, 0x0d25: 0x10b8, 0x0d26: 0x10bf, 0x0d27: 0x10c6, 0x0d28: 0x10cd, 0x0d29: 0x10d2, - 0x0d2a: 0x10d7, 0x0d2b: 0x10de, 0x0d2c: 0x10e5, 0x0d2d: 0x10ec, 0x0d2e: 0x10f3, 0x0d2f: 0x10fa, - 0x0d30: 0x1101, 0x0d31: 0x0525, 0x0d32: 0x1106, 0x0d33: 0x052a, 0x0d34: 0x110b, 0x0d35: 0x052f, - 0x0d36: 0x1110, 0x0d37: 0x0534, 0x0d38: 0x1115, 0x0d39: 0x054a, 0x0d3a: 0x111a, 0x0d3b: 0x054f, - 0x0d3c: 0x111f, 0x0d3d: 0x0554, + 0x0d00: 0x057e, 0x0d01: 0x3b6a, 0x0d02: 0x3b6d, 0x0d03: 0x055c, 0x0d04: 0x12fd, 0x0d05: 0x0572, + 0x0d06: 0x3b70, 0x0d07: 0x0015, 0x0d08: 0x3b73, 0x0d09: 0x3b76, 0x0d0a: 0x3b79, 0x0d0b: 0x056f, + 0x0d0c: 0x0575, 0x0d0d: 0x0578, 0x0d0e: 0x3b7c, 0x0d0f: 0x3b7f, 0x0d10: 0x3b82, 0x0d11: 0x056c, + 0x0d12: 0x0979, 0x0d13: 0x3b85, 0x0d14: 0x3b88, 0x0d15: 0x3b8b, 0x0d16: 0x057e, 0x0d17: 0x055c, + 0x0d18: 0x0572, 0x0d19: 0x056c, 0x0d1a: 0x0575, 0x0d1b: 0x056f, 0x0d1c: 0x3b2d, 0x0d1d: 0x3b30, + 0x0d1e: 0x14eb, 0x0d1f: 0x3b33, 0x0d20: 0x3b36, 0x0d21: 0x3b39, 0x0d22: 0x3b3c, 0x0d23: 0x057b, + 0x0d24: 0x3b3f, 0x0d25: 0x3b42, 0x0d26: 0x3b45, 0x0d27: 0x3b48, 0x0d28: 0x3b4b, 0x0d29: 0x3b4e, + 0x0d2a: 0x3b51, 0x0d2b: 0x14ee, 0x0d2c: 0x3b54, 0x0d2d: 0x057b, 0x0d2e: 0x0581, 0x0d2f: 0x3b57, + 0x0d30: 0x055f, 0x0d31: 0x3b5a, 0x0d32: 0x3b5d, 0x0d33: 0x3b60, 0x0d34: 0x14d6, 0x0d35: 0x3b63, + 0x0d36: 0x3b67, 0x0d37: 0x0559, 0x0d38: 0x0973, 0x0d39: 0x0976, 0x0d3a: 0x057e, 0x0d3b: 0x3b6a, + 0x0d3c: 0x3b6d, 0x0d3d: 0x055c, 0x0d3e: 0x12fd, 0x0d3f: 0x0572, // Block 0x35, offset 0xd40 - 0x0d40: 0x1124, 0x0d41: 0x112b, 0x0d42: 0x1132, 0x0d43: 0x113b, 0x0d44: 0x1144, 0x0d45: 0x114d, - 0x0d46: 0x1156, 0x0d47: 0x115f, 0x0d48: 0x1168, 0x0d49: 0x116f, 0x0d4a: 0x1176, 0x0d4b: 0x117f, - 0x0d4c: 0x1188, 0x0d4d: 0x1191, 0x0d4e: 0x119a, 0x0d4f: 0x11a3, 0x0d50: 0x11ac, 0x0d51: 0x11b3, - 0x0d52: 0x11ba, 0x0d53: 0x11c3, 0x0d54: 0x11cc, 0x0d55: 0x11d5, 0x0d56: 0x11de, 0x0d57: 0x11e7, - 0x0d58: 0x11f0, 0x0d59: 0x11f7, 0x0d5a: 0x11fe, 0x0d5b: 0x1207, 0x0d5c: 0x1210, 0x0d5d: 0x1219, - 0x0d5e: 0x1222, 0x0d5f: 0x122b, 0x0d60: 0x1234, 0x0d61: 0x123b, 0x0d62: 0x1242, 0x0d63: 0x124b, - 0x0d64: 0x1254, 0x0d65: 0x125d, 0x0d66: 0x1266, 0x0d67: 0x126f, 0x0d68: 0x1278, 0x0d69: 0x127f, - 0x0d6a: 0x1286, 0x0d6b: 0x128f, 0x0d6c: 0x1298, 0x0d6d: 0x12a1, 0x0d6e: 0x12aa, 0x0d6f: 0x12b3, - 0x0d70: 0x12bc, 0x0d71: 0x12c1, 0x0d72: 0x12c6, 0x0d73: 0x12cd, 0x0d74: 0x12d2, - 0x0d76: 0x12d9, 0x0d77: 0x12de, 0x0d78: 0x12e5, 0x0d79: 0x12ea, 0x0d7a: 0x12ef, 0x0d7b: 0x04ee, - 0x0d7c: 0x12f4, 0x0d7d: 0x12f9, 0x0d7e: 0x12fd, 0x0d7f: 0x12f9, + 0x0d40: 0x3b70, 0x0d41: 0x0015, 0x0d42: 0x3b73, 0x0d43: 0x3b76, 0x0d44: 0x3b79, 0x0d45: 0x056f, + 0x0d46: 0x0575, 0x0d47: 0x0578, 0x0d48: 0x3b7c, 0x0d49: 0x3b7f, 0x0d4a: 0x3b82, 0x0d4b: 0x056c, + 0x0d4c: 0x0979, 0x0d4d: 0x3b85, 0x0d4e: 0x3b88, 0x0d4f: 0x3b8b, 0x0d50: 0x057e, 0x0d51: 0x055c, + 0x0d52: 0x0572, 0x0d53: 0x056c, 0x0d54: 0x0575, 0x0d55: 0x056f, 0x0d56: 0x3b2d, 0x0d57: 0x3b30, + 0x0d58: 0x14eb, 0x0d59: 0x3b33, 0x0d5a: 0x3b36, 0x0d5b: 0x3b39, 0x0d5c: 0x3b3c, 0x0d5d: 0x057b, + 0x0d5e: 0x3b3f, 0x0d5f: 0x3b42, 0x0d60: 0x3b45, 0x0d61: 0x3b48, 0x0d62: 0x3b4b, 0x0d63: 0x3b4e, + 0x0d64: 0x3b51, 0x0d65: 0x14ee, 0x0d66: 0x3b54, 0x0d67: 0x057b, 0x0d68: 0x0581, 0x0d69: 0x3b57, + 0x0d6a: 0x055f, 0x0d6b: 0x3b5a, 0x0d6c: 0x3b5d, 0x0d6d: 0x3b60, 0x0d6e: 0x14d6, 0x0d6f: 0x3b63, + 0x0d70: 0x3b67, 0x0d71: 0x0559, 0x0d72: 0x0973, 0x0d73: 0x0976, 0x0d74: 0x057e, 0x0d75: 0x3b6a, + 0x0d76: 0x3b6d, 0x0d77: 0x055c, 0x0d78: 0x12fd, 0x0d79: 0x0572, 0x0d7a: 0x3b70, 0x0d7b: 0x0015, + 0x0d7c: 0x3b73, 0x0d7d: 0x3b76, 0x0d7e: 0x3b79, 0x0d7f: 0x056f, // Block 0x36, offset 0xd80 - 0x0d80: 0x1300, 0x0d81: 0x1309, 0x0d82: 0x130f, 0x0d83: 0x1316, 0x0d84: 0x131b, - 0x0d86: 0x1322, 0x0d87: 0x1327, 0x0d88: 0x132e, 0x0d89: 0x04f6, 0x0d8a: 0x1333, 0x0d8b: 0x04fb, - 0x0d8c: 0x1338, 0x0d8d: 0x1343, 0x0d8e: 0x134f, 0x0d8f: 0x135b, 0x0d90: 0x1361, 0x0d91: 0x1366, - 0x0d92: 0x136b, 0x0d93: 0x0514, 0x0d96: 0x1372, 0x0d97: 0x1377, - 0x0d98: 0x137e, 0x0d99: 0x1383, 0x0d9a: 0x1388, 0x0d9b: 0x0500, 0x0d9d: 0x1393, - 0x0d9e: 0x139f, 0x0d9f: 0x13ab, 0x0da0: 0x13b1, 0x0da1: 0x13b6, 0x0da2: 0x13bb, 0x0da3: 0x0539, - 0x0da4: 0x13c2, 0x0da5: 0x13c7, 0x0da6: 0x13cc, 0x0da7: 0x13d1, 0x0da8: 0x13d8, 0x0da9: 0x13dd, - 0x0daa: 0x13e2, 0x0dab: 0x050a, 0x0dac: 0x13e7, 0x0dad: 0x13f1, 0x0dae: 0x04e8, 0x0daf: 0x13f7, - 0x0db2: 0x13f9, 0x0db3: 0x1400, 0x0db4: 0x1405, - 0x0db6: 0x140c, 0x0db7: 0x1411, 0x0db8: 0x1418, 0x0db9: 0x0505, 0x0dba: 0x141d, 0x0dbb: 0x050f, - 0x0dbc: 0x1422, 0x0dbd: 0x0011, 0x0dbe: 0x142a, + 0x0d80: 0x0575, 0x0d81: 0x0578, 0x0d82: 0x3b7c, 0x0d83: 0x3b7f, 0x0d84: 0x3b82, 0x0d85: 0x056c, + 0x0d86: 0x0979, 0x0d87: 0x3b85, 0x0d88: 0x3b88, 0x0d89: 0x3b8b, 0x0d8a: 0x057e, 0x0d8b: 0x055c, + 0x0d8c: 0x0572, 0x0d8d: 0x056c, 0x0d8e: 0x0575, 0x0d8f: 0x056f, 0x0d90: 0x3b2d, 0x0d91: 0x3b30, + 0x0d92: 0x14eb, 0x0d93: 0x3b33, 0x0d94: 0x3b36, 0x0d95: 0x3b39, 0x0d96: 0x3b3c, 0x0d97: 0x057b, + 0x0d98: 0x3b3f, 0x0d99: 0x3b42, 0x0d9a: 0x3b45, 0x0d9b: 0x3b48, 0x0d9c: 0x3b4b, 0x0d9d: 0x3b4e, + 0x0d9e: 0x3b51, 0x0d9f: 0x14ee, 0x0da0: 0x3b54, 0x0da1: 0x057b, 0x0da2: 0x0581, 0x0da3: 0x3b57, + 0x0da4: 0x055f, 0x0da5: 0x3b5a, 0x0da6: 0x3b5d, 0x0da7: 0x3b60, 0x0da8: 0x14d6, 0x0da9: 0x3b63, + 0x0daa: 0x3b67, 0x0dab: 0x0559, 0x0dac: 0x0973, 0x0dad: 0x0976, 0x0dae: 0x057e, 0x0daf: 0x3b6a, + 0x0db0: 0x3b6d, 0x0db1: 0x055c, 0x0db2: 0x12fd, 0x0db3: 0x0572, 0x0db4: 0x3b70, 0x0db5: 0x0015, + 0x0db6: 0x3b73, 0x0db7: 0x3b76, 0x0db8: 0x3b79, 0x0db9: 0x056f, 0x0dba: 0x0575, 0x0dbb: 0x0578, + 0x0dbc: 0x3b7c, 0x0dbd: 0x3b7f, 0x0dbe: 0x3b82, 0x0dbf: 0x056c, // Block 0x37, offset 0xdc0 - 0x0dc0: 0x0001, 0x0dc1: 0x0001, 0x0dc2: 0x0001, 0x0dc3: 0x0001, 0x0dc4: 0x0001, 0x0dc5: 0x0001, - 0x0dc6: 0x0001, 0x0dc7: 0x0001, 0x0dc8: 0x0001, 0x0dc9: 0x0001, 0x0dca: 0x0001, - 0x0dd1: 0x1436, - 0x0dd7: 0x143a, - 0x0de4: 0x143e, 0x0de5: 0x1440, 0x0de6: 0x1443, - 0x0def: 0x0001, - 0x0df3: 0x1447, 0x0df4: 0x144e, - 0x0df6: 0x1458, 0x0df7: 0x145f, - 0x0dfc: 0x1469, 0x0dfe: 0x146c, + 0x0dc0: 0x0979, 0x0dc1: 0x3b85, 0x0dc2: 0x3b88, 0x0dc3: 0x3b8b, 0x0dc4: 0x057e, 0x0dc5: 0x055c, + 0x0dc6: 0x0572, 0x0dc7: 0x056c, 0x0dc8: 0x0575, 0x0dc9: 0x056f, 0x0dca: 0x3b8f, 0x0dcb: 0x3b92, + 0x0dce: 0x1486, 0x0dcf: 0x001c, 0x0dd0: 0x000d, 0x0dd1: 0x000f, + 0x0dd2: 0x1488, 0x0dd3: 0x148a, 0x0dd4: 0x148c, 0x0dd5: 0x148e, 0x0dd6: 0x1490, 0x0dd7: 0x1492, + 0x0dd8: 0x1486, 0x0dd9: 0x001c, 0x0dda: 0x000d, 0x0ddb: 0x000f, 0x0ddc: 0x1488, 0x0ddd: 0x148a, + 0x0dde: 0x148c, 0x0ddf: 0x148e, 0x0de0: 0x1490, 0x0de1: 0x1492, 0x0de2: 0x1486, 0x0de3: 0x001c, + 0x0de4: 0x000d, 0x0de5: 0x000f, 0x0de6: 0x1488, 0x0de7: 0x148a, 0x0de8: 0x148c, 0x0de9: 0x148e, + 0x0dea: 0x1490, 0x0deb: 0x1492, 0x0dec: 0x1486, 0x0ded: 0x001c, 0x0dee: 0x000d, 0x0def: 0x000f, + 0x0df0: 0x1488, 0x0df1: 0x148a, 0x0df2: 0x148c, 0x0df3: 0x148e, 0x0df4: 0x1490, 0x0df5: 0x1492, + 0x0df6: 0x1486, 0x0df7: 0x001c, 0x0df8: 0x000d, 0x0df9: 0x000f, 0x0dfa: 0x1488, 0x0dfb: 0x148a, + 0x0dfc: 0x148c, 0x0dfd: 0x148e, 0x0dfe: 0x1490, 0x0dff: 0x1492, // Block 0x38, offset 0xe00 - 0x0e07: 0x1470, 0x0e08: 0x1473, 0x0e09: 0x1476, - 0x0e17: 0x1479, - 0x0e1f: 0x0001, - 0x0e30: 0x1486, 0x0e31: 0x097c, 0x0e34: 0x1488, 0x0e35: 0x148a, - 0x0e36: 0x148c, 0x0e37: 0x148e, 0x0e38: 0x1490, 0x0e39: 0x1492, 0x0e3a: 0x1494, 0x0e3b: 0x1496, - 0x0e3c: 0x149a, 0x0e3d: 0x149c, 0x0e3e: 0x149e, 0x0e3f: 0x14a0, + 0x0e00: 0x3b95, 0x0e01: 0x3b98, 0x0e02: 0x3b9b, 0x0e03: 0x3b9e, 0x0e04: 0x3ba1, 0x0e05: 0x3ba4, + 0x0e06: 0x3ba7, 0x0e07: 0x3baa, 0x0e08: 0x3bad, 0x0e09: 0x3bb0, 0x0e0a: 0x3bb3, + 0x0e10: 0x3bb6, 0x0e11: 0x3bba, + 0x0e12: 0x3bbe, 0x0e13: 0x3bc2, 0x0e14: 0x3bc6, 0x0e15: 0x3bca, 0x0e16: 0x3bce, 0x0e17: 0x3bd2, + 0x0e18: 0x3bd6, 0x0e19: 0x3bda, 0x0e1a: 0x3bde, 0x0e1b: 0x3be2, 0x0e1c: 0x3be6, 0x0e1d: 0x3bea, + 0x0e1e: 0x3bee, 0x0e1f: 0x3bf2, 0x0e20: 0x3bf6, 0x0e21: 0x3bfa, 0x0e22: 0x3bfe, 0x0e23: 0x3c02, + 0x0e24: 0x3c06, 0x0e25: 0x3c0a, 0x0e26: 0x3c0e, 0x0e27: 0x3c12, 0x0e28: 0x3c16, 0x0e29: 0x3c1a, + 0x0e2a: 0x3c1e, 0x0e2b: 0x14ad, 0x0e2c: 0x092b, 0x0e2d: 0x3c26, 0x0e2e: 0x3c29, + 0x0e30: 0x0906, 0x0e31: 0x090b, 0x0e32: 0x14ad, 0x0e33: 0x090d, 0x0e34: 0x090f, 0x0e35: 0x14d9, + 0x0e36: 0x0914, 0x0e37: 0x0916, 0x0e38: 0x0918, 0x0e39: 0x091a, 0x0e3a: 0x091c, 0x0e3b: 0x091e, + 0x0e3c: 0x0920, 0x0e3d: 0x0922, 0x0e3e: 0x0924, 0x0e3f: 0x0929, // Block 0x39, offset 0xe40 - 0x0e40: 0x1486, 0x0e41: 0x001c, 0x0e42: 0x000d, 0x0e43: 0x000f, 0x0e44: 0x1488, 0x0e45: 0x148a, - 0x0e46: 0x148c, 0x0e47: 0x148e, 0x0e48: 0x1490, 0x0e49: 0x1492, 0x0e4a: 0x1494, 0x0e4b: 0x1496, - 0x0e4c: 0x149a, 0x0e4d: 0x149c, 0x0e4e: 0x149e, 0x0e50: 0x0007, 0x0e51: 0x0941, - 0x0e52: 0x001e, 0x0e53: 0x04c7, 0x0e54: 0x0943, 0x0e55: 0x0494, 0x0e56: 0x094e, 0x0e57: 0x04c5, - 0x0e58: 0x0950, 0x0e59: 0x14a0, 0x0e5a: 0x0960, 0x0e5b: 0x02c8, 0x0e5c: 0x0962, - 0x0e68: 0x14a2, + 0x0e40: 0x3c3f, 0x0e41: 0x3c46, 0x0e42: 0x2291, + 0x0e50: 0x1922, 0x0e51: 0x3c4d, + 0x0e52: 0x3c51, 0x0e53: 0x1cb3, 0x0e54: 0x183e, 0x0e55: 0x3c55, 0x0e56: 0x3c59, 0x0e57: 0x1ed0, + 0x0e58: 0x3c5d, 0x0e59: 0x3c61, 0x0e5a: 0x3c65, 0x0e5b: 0x2d49, 0x0e5c: 0x3c69, 0x0e5d: 0x3c6d, + 0x0e5e: 0x3c71, 0x0e5f: 0x3c75, 0x0e60: 0x3c79, 0x0e61: 0x3c7d, 0x0e62: 0x19b2, 0x0e63: 0x3c81, + 0x0e64: 0x3c85, 0x0e65: 0x3c89, 0x0e66: 0x3c8d, 0x0e67: 0x3c91, 0x0e68: 0x3c95, 0x0e69: 0x1826, + 0x0e6a: 0x1eb0, 0x0e6b: 0x3c99, 0x0e6c: 0x21c7, 0x0e6d: 0x1ebc, 0x0e6e: 0x21cb, 0x0e6f: 0x3c9d, + 0x0e70: 0x1a92, 0x0e71: 0x3ca1, 0x0e72: 0x3ca5, 0x0e73: 0x3ca9, 0x0e74: 0x3cad, 0x0e75: 0x3cb1, + 0x0e76: 0x2183, 0x0e77: 0x194a, 0x0e78: 0x3cb5, 0x0e79: 0x3cb9, 0x0e7a: 0x3cbd, // Block 0x3a, offset 0xe80 - 0x0e80: 0x14a5, 0x0e81: 0x14a9, 0x0e82: 0x14ad, 0x0e83: 0x14af, 0x0e85: 0x14b3, - 0x0e86: 0x14b7, 0x0e87: 0x14bb, 0x0e89: 0x14be, 0x0e8a: 0x094c, 0x0e8b: 0x0916, - 0x0e8c: 0x0916, 0x0e8d: 0x0916, 0x0e8e: 0x0494, 0x0e8f: 0x14c2, 0x0e90: 0x0918, 0x0e91: 0x0918, - 0x0e92: 0x091e, 0x0e93: 0x04c5, 0x0e95: 0x0922, 0x0e96: 0x14c5, - 0x0e99: 0x0929, 0x0e9a: 0x14c8, 0x0e9b: 0x092b, 0x0e9c: 0x092b, 0x0e9d: 0x092b, - 0x0ea0: 0x14ca, 0x0ea1: 0x14cd, 0x0ea2: 0x14d1, - 0x0ea4: 0x14d4, 0x0ea6: 0x14d6, 0x0ea8: 0x14d4, - 0x0eaa: 0x091c, 0x0eab: 0x0046, 0x0eac: 0x090b, 0x0ead: 0x14ad, 0x0eaf: 0x0941, - 0x0eb0: 0x090f, 0x0eb1: 0x14d9, 0x0eb3: 0x0920, 0x0eb4: 0x001e, 0x0eb5: 0x14db, - 0x0eb6: 0x14de, 0x0eb7: 0x14e1, 0x0eb8: 0x14e4, 0x0eb9: 0x097c, 0x0ebb: 0x14e7, - 0x0ebc: 0x056f, 0x0ebd: 0x0973, 0x0ebe: 0x14eb, 0x0ebf: 0x14ee, + 0x0e80: 0x3d23, 0x0e81: 0x3d27, 0x0e82: 0x3d2b, 0x0e83: 0x3d2f, 0x0e84: 0x3d34, 0x0e85: 0x2eb5, + 0x0e86: 0x3d38, 0x0e87: 0x3d3c, 0x0e88: 0x3d40, 0x0e89: 0x3d44, 0x0e8a: 0x2eb9, 0x0e8b: 0x3d48, + 0x0e8c: 0x3d4c, 0x0e8d: 0x3d50, 0x0e8e: 0x2ebd, 0x0e8f: 0x3d55, 0x0e90: 0x3d59, 0x0e91: 0x3d5d, + 0x0e92: 0x3d61, 0x0e93: 0x3d66, 0x0e94: 0x3d6a, 0x0e95: 0x3c71, 0x0e96: 0x3d6e, 0x0e97: 0x3d73, + 0x0e98: 0x3d77, 0x0e99: 0x3d7b, 0x0e9a: 0x3d7f, 0x0e9b: 0x2f9a, 0x0e9c: 0x3d83, 0x0e9d: 0x1866, + 0x0e9e: 0x3d88, 0x0e9f: 0x3d8c, 0x0ea0: 0x3d90, 0x0ea1: 0x3d94, 0x0ea2: 0x3cb9, 0x0ea3: 0x3d98, + 0x0ea4: 0x3d9c, 0x0ea5: 0x2fae, 0x0ea6: 0x2ec1, 0x0ea7: 0x2ec5, 0x0ea8: 0x2fb2, 0x0ea9: 0x3da0, + 0x0eaa: 0x3da4, 0x0eab: 0x2bf1, 0x0eac: 0x3da8, 0x0ead: 0x2ec9, 0x0eae: 0x3dac, 0x0eaf: 0x3db0, + 0x0eb0: 0x3db4, 0x0eb1: 0x3db8, 0x0eb2: 0x3db8, 0x0eb3: 0x3db8, 0x0eb4: 0x3dbc, 0x0eb5: 0x3dc1, + 0x0eb6: 0x3dc5, 0x0eb7: 0x3dc9, 0x0eb8: 0x3dcd, 0x0eb9: 0x3dd2, 0x0eba: 0x3dd6, 0x0ebb: 0x3dda, + 0x0ebc: 0x3dde, 0x0ebd: 0x3de2, 0x0ebe: 0x3de6, 0x0ebf: 0x3dea, // Block 0x3b, offset 0xec0 - 0x0ec0: 0x14f1, 0x0ec5: 0x090d, - 0x0ec6: 0x093f, 0x0ec7: 0x0941, 0x0ec8: 0x097c, 0x0ec9: 0x0499, - 0x0ed0: 0x14f5, 0x0ed1: 0x14fb, - 0x0ed2: 0x1501, 0x0ed3: 0x1508, 0x0ed4: 0x150e, 0x0ed5: 0x1514, 0x0ed6: 0x151a, 0x0ed7: 0x1520, - 0x0ed8: 0x1526, 0x0ed9: 0x152c, 0x0eda: 0x1532, 0x0edb: 0x1538, 0x0edc: 0x153e, 0x0edd: 0x1544, - 0x0ede: 0x154a, 0x0edf: 0x1550, 0x0ee0: 0x0918, 0x0ee1: 0x1555, 0x0ee2: 0x1558, 0x0ee3: 0x155c, - 0x0ee4: 0x155f, 0x0ee5: 0x1561, 0x0ee6: 0x1564, 0x0ee7: 0x1568, 0x0ee8: 0x156d, 0x0ee9: 0x1570, - 0x0eea: 0x1572, 0x0eeb: 0x1575, 0x0eec: 0x091e, 0x0eed: 0x14ad, 0x0eee: 0x090d, 0x0eef: 0x0920, - 0x0ef0: 0x097c, 0x0ef1: 0x1579, 0x0ef2: 0x157c, 0x0ef3: 0x1580, 0x0ef4: 0x096d, 0x0ef5: 0x1583, - 0x0ef6: 0x1586, 0x0ef7: 0x158a, 0x0ef8: 0x158f, 0x0ef9: 0x04c7, 0x0efa: 0x1592, 0x0efb: 0x1595, - 0x0efc: 0x04c5, 0x0efd: 0x0984, 0x0efe: 0x093f, 0x0eff: 0x0950, + 0x0ec0: 0x3dee, 0x0ec1: 0x3df2, 0x0ec2: 0x3df6, 0x0ec3: 0x3dfa, 0x0ec4: 0x3dfe, 0x0ec5: 0x3e02, + 0x0ec6: 0x3e02, 0x0ec7: 0x2fba, 0x0ec8: 0x3e06, 0x0ec9: 0x3e0a, 0x0eca: 0x3e0e, 0x0ecb: 0x3e12, + 0x0ecc: 0x2ed1, 0x0ecd: 0x3e16, 0x0ece: 0x3e1a, 0x0ecf: 0x3e1e, 0x0ed0: 0x2e39, 0x0ed1: 0x3e22, + 0x0ed2: 0x3e26, 0x0ed3: 0x3e2a, 0x0ed4: 0x3e2e, 0x0ed5: 0x3e32, 0x0ed6: 0x3e36, 0x0ed7: 0x3e3a, + 0x0ed8: 0x3e3e, 0x0ed9: 0x3e42, 0x0eda: 0x3e47, 0x0edb: 0x3e4b, 0x0edc: 0x3e4f, 0x0edd: 0x3c55, + 0x0ede: 0x3e53, 0x0edf: 0x3e57, 0x0ee0: 0x3e5b, 0x0ee1: 0x3e60, 0x0ee2: 0x3e65, 0x0ee3: 0x3e69, + 0x0ee4: 0x3e6d, 0x0ee5: 0x3e71, 0x0ee6: 0x3e75, 0x0ee7: 0x3e79, 0x0ee8: 0x3e7d, 0x0ee9: 0x3e81, + 0x0eea: 0x3e85, 0x0eeb: 0x3e85, 0x0eec: 0x3e89, 0x0eed: 0x3e8e, 0x0eee: 0x3e92, 0x0eef: 0x2be1, + 0x0ef0: 0x3e96, 0x0ef1: 0x3e9a, 0x0ef2: 0x3e9f, 0x0ef3: 0x3ea3, 0x0ef4: 0x3ea7, 0x0ef5: 0x18ce, + 0x0ef6: 0x3eab, 0x0ef7: 0x3eaf, 0x0ef8: 0x18d6, 0x0ef9: 0x3eb3, 0x0efa: 0x3eb7, 0x0efb: 0x3ebb, + 0x0efc: 0x3ec0, 0x0efd: 0x3ec4, 0x0efe: 0x3ec9, 0x0eff: 0x3ecd, // Block 0x3c, offset 0xf00 - 0x0f09: 0x1599, - 0x0f1a: 0x159f, 0x0f1b: 0x15a5, - 0x0f2e: 0x15ab, + 0x0f00: 0x3ed1, 0x0f01: 0x3ed5, 0x0f02: 0x3ed9, 0x0f03: 0x3edd, 0x0f04: 0x3ee1, 0x0f05: 0x3ee5, + 0x0f06: 0x3ee9, 0x0f07: 0x3eed, 0x0f08: 0x3ef1, 0x0f09: 0x3ef5, 0x0f0a: 0x3efa, 0x0f0b: 0x3efe, + 0x0f0c: 0x3f02, 0x0f0d: 0x3f06, 0x0f0e: 0x2b11, 0x0f0f: 0x3f0a, 0x0f10: 0x18fe, 0x0f11: 0x3f0f, + 0x0f12: 0x3f0f, 0x0f13: 0x3f14, 0x0f14: 0x3f18, 0x0f15: 0x3f18, 0x0f16: 0x3f1c, 0x0f17: 0x3f20, + 0x0f18: 0x3f25, 0x0f19: 0x3f2a, 0x0f1a: 0x3f2e, 0x0f1b: 0x3f32, 0x0f1c: 0x3f36, 0x0f1d: 0x3f3a, + 0x0f1e: 0x3f3e, 0x0f1f: 0x3f42, 0x0f20: 0x3f46, 0x0f21: 0x3f4a, 0x0f22: 0x3f4e, 0x0f23: 0x2ee5, + 0x0f24: 0x3f52, 0x0f25: 0x3f57, 0x0f26: 0x3f5b, 0x0f27: 0x3f5f, 0x0f28: 0x2fea, 0x0f29: 0x3f5f, + 0x0f2a: 0x3f63, 0x0f2b: 0x2eed, 0x0f2c: 0x3f67, 0x0f2d: 0x3f6b, 0x0f2e: 0x3f6f, 0x0f2f: 0x3f73, + 0x0f30: 0x2ef1, 0x0f31: 0x2aa5, 0x0f32: 0x3f77, 0x0f33: 0x3f7b, 0x0f34: 0x3f7f, 0x0f35: 0x3f83, + 0x0f36: 0x3f87, 0x0f37: 0x3f8b, 0x0f38: 0x3f8f, 0x0f39: 0x3f94, 0x0f3a: 0x3f98, 0x0f3b: 0x3f9c, + 0x0f3c: 0x3fa0, 0x0f3d: 0x3fa4, 0x0f3e: 0x3fa8, 0x0f3f: 0x3fad, // Block 0x3d, offset 0xf40 - 0x0f4d: 0x15b1, 0x0f4e: 0x15b7, 0x0f4f: 0x15bd, + 0x0f40: 0x3fb1, 0x0f41: 0x3fb5, 0x0f42: 0x3fb9, 0x0f43: 0x3fbd, 0x0f44: 0x3fc1, 0x0f45: 0x3fc5, + 0x0f46: 0x3fc9, 0x0f47: 0x3fcd, 0x0f48: 0x2ef5, 0x0f49: 0x3fd1, 0x0f4a: 0x3fd5, 0x0f4b: 0x3fda, + 0x0f4c: 0x3fde, 0x0f4d: 0x3fe2, 0x0f4e: 0x3fe6, 0x0f4f: 0x2efd, 0x0f50: 0x3fea, 0x0f51: 0x3fee, + 0x0f52: 0x3ff2, 0x0f53: 0x3ff6, 0x0f54: 0x3ffa, 0x0f55: 0x3ffe, 0x0f56: 0x4002, 0x0f57: 0x4006, + 0x0f58: 0x2b15, 0x0f59: 0x300a, 0x0f5a: 0x400a, 0x0f5b: 0x400e, 0x0f5c: 0x4012, 0x0f5d: 0x4016, + 0x0f5e: 0x401b, 0x0f5f: 0x401f, 0x0f60: 0x4023, 0x0f61: 0x4027, 0x0f62: 0x2f01, 0x0f63: 0x402b, + 0x0f64: 0x4030, 0x0f65: 0x4034, 0x0f66: 0x4038, 0x0f67: 0x30b5, 0x0f68: 0x403c, 0x0f69: 0x4040, + 0x0f6a: 0x4044, 0x0f6b: 0x4048, 0x0f6c: 0x404c, 0x0f6d: 0x4051, 0x0f6e: 0x4055, 0x0f6f: 0x4059, + 0x0f70: 0x405d, 0x0f71: 0x4062, 0x0f72: 0x4066, 0x0f73: 0x406a, 0x0f74: 0x406e, 0x0f75: 0x2c25, + 0x0f76: 0x4072, 0x0f77: 0x4076, 0x0f78: 0x407b, 0x0f79: 0x4080, 0x0f7a: 0x4085, 0x0f7b: 0x4089, + 0x0f7c: 0x408e, 0x0f7d: 0x4092, 0x0f7e: 0x4096, 0x0f7f: 0x409a, // Block 0x3e, offset 0xf80 - 0x0f84: 0x15c3, - 0x0f89: 0x15c9, - 0x0f8c: 0x15cf, - 0x0fa4: 0x15d5, 0x0fa6: 0x15db, - 0x0fac: 0x15e1, 0x0fad: 0x15e8, 0x0faf: 0x15f2, - 0x0fb0: 0x15f9, + 0x0f80: 0x409e, 0x0f81: 0x2f05, 0x0f82: 0x2d71, 0x0f83: 0x40a2, 0x0f84: 0x40a6, 0x0f85: 0x40aa, + 0x0f86: 0x40ae, 0x0f87: 0x40b3, 0x0f88: 0x40b7, 0x0f89: 0x40bb, 0x0f8a: 0x40bf, 0x0f8b: 0x3016, + 0x0f8c: 0x40c3, 0x0f8d: 0x40c7, 0x0f8e: 0x40cc, 0x0f8f: 0x40d0, 0x0f90: 0x40d4, 0x0f91: 0x40d9, + 0x0f92: 0x40de, 0x0f93: 0x40e2, 0x0f94: 0x301a, 0x0f95: 0x40e6, 0x0f96: 0x40ea, 0x0f97: 0x40ee, + 0x0f98: 0x40f2, 0x0f99: 0x40f6, 0x0f9a: 0x40fa, 0x0f9b: 0x40fe, 0x0f9c: 0x4103, 0x0f9d: 0x4107, + 0x0f9e: 0x410c, 0x0f9f: 0x4110, 0x0fa0: 0x4115, 0x0fa1: 0x3022, 0x0fa2: 0x4119, 0x0fa3: 0x411d, + 0x0fa4: 0x4122, 0x0fa5: 0x4126, 0x0fa6: 0x412a, 0x0fa7: 0x412f, 0x0fa8: 0x4134, 0x0fa9: 0x4138, + 0x0faa: 0x413c, 0x0fab: 0x4140, 0x0fac: 0x4144, 0x0fad: 0x4144, 0x0fae: 0x4148, 0x0faf: 0x414c, + 0x0fb0: 0x302a, 0x0fb1: 0x4150, 0x0fb2: 0x4154, 0x0fb3: 0x4158, 0x0fb4: 0x415c, 0x0fb5: 0x4160, + 0x0fb6: 0x4165, 0x0fb7: 0x4169, 0x0fb8: 0x2bed, 0x0fb9: 0x416e, 0x0fba: 0x4173, 0x0fbb: 0x4177, + 0x0fbc: 0x417c, 0x0fbd: 0x4181, 0x0fbe: 0x4186, 0x0fbf: 0x418a, // Block 0x3f, offset 0xfc0 - 0x0fc1: 0x1603, 0x0fc4: 0x1609, - 0x0fc7: 0x160f, 0x0fc9: 0x1615, - 0x0fe0: 0x161b, 0x0fe2: 0x161f, - 0x0fed: 0x1625, 0x0fee: 0x162b, 0x0fef: 0x162f, - 0x0ff0: 0x1633, 0x0ff1: 0x1639, 0x0ff4: 0x163f, 0x0ff5: 0x1645, - 0x0ff8: 0x164b, 0x0ff9: 0x1651, + 0x0fc0: 0x3042, 0x0fc1: 0x418e, 0x0fc2: 0x4193, 0x0fc3: 0x4198, 0x0fc4: 0x419d, 0x0fc5: 0x41a2, + 0x0fc6: 0x41a6, 0x0fc7: 0x41a6, 0x0fc8: 0x3046, 0x0fc9: 0x30bd, 0x0fca: 0x41aa, 0x0fcb: 0x41ae, + 0x0fcc: 0x41b2, 0x0fcd: 0x41b6, 0x0fce: 0x41bb, 0x0fcf: 0x2b59, 0x0fd0: 0x304e, 0x0fd1: 0x41bf, + 0x0fd2: 0x41c3, 0x0fd3: 0x2f2d, 0x0fd4: 0x41c8, 0x0fd5: 0x41cd, 0x0fd6: 0x2e89, 0x0fd7: 0x41d2, + 0x0fd8: 0x41d6, 0x0fd9: 0x2f39, 0x0fda: 0x41da, 0x0fdb: 0x41de, 0x0fdc: 0x41e2, 0x0fdd: 0x41e7, + 0x0fde: 0x41e7, 0x0fdf: 0x41ec, 0x0fe0: 0x41f0, 0x0fe1: 0x41f4, 0x0fe2: 0x41f9, 0x0fe3: 0x41fd, + 0x0fe4: 0x4201, 0x0fe5: 0x4205, 0x0fe6: 0x420a, 0x0fe7: 0x420e, 0x0fe8: 0x4212, 0x0fe9: 0x4216, + 0x0fea: 0x421a, 0x0feb: 0x421e, 0x0fec: 0x4223, 0x0fed: 0x4227, 0x0fee: 0x422b, 0x0fef: 0x422f, + 0x0ff0: 0x4233, 0x0ff1: 0x4237, 0x0ff2: 0x423b, 0x0ff3: 0x4240, 0x0ff4: 0x4245, 0x0ff5: 0x4249, + 0x0ff6: 0x424e, 0x0ff7: 0x4252, 0x0ff8: 0x4257, 0x0ff9: 0x425b, 0x0ffa: 0x2f51, 0x0ffb: 0x425f, + 0x0ffc: 0x4264, 0x0ffd: 0x4269, 0x0ffe: 0x426d, 0x0fff: 0x4272, // Block 0x40, offset 0x1000 - 0x1000: 0x1657, 0x1001: 0x165d, 0x1004: 0x1663, 0x1005: 0x1669, - 0x1008: 0x166f, 0x1009: 0x1675, - 0x102c: 0x167b, 0x102d: 0x1681, 0x102e: 0x1687, 0x102f: 0x168d, + 0x1000: 0x4276, 0x1001: 0x427b, 0x1002: 0x427f, 0x1003: 0x4283, 0x1004: 0x4287, 0x1005: 0x428b, + 0x1006: 0x428f, 0x1007: 0x4293, 0x1008: 0x4298, 0x1009: 0x429d, 0x100a: 0x42a2, 0x100b: 0x3f14, + 0x100c: 0x42a7, 0x100d: 0x42ab, 0x100e: 0x42af, 0x100f: 0x42b3, 0x1010: 0x42b7, 0x1011: 0x42bb, + 0x1012: 0x42bf, 0x1013: 0x42c3, 0x1014: 0x42c7, 0x1015: 0x42cb, 0x1016: 0x42cf, 0x1017: 0x42d3, + 0x1018: 0x2c31, 0x1019: 0x42d8, 0x101a: 0x42dc, 0x101b: 0x42e0, 0x101c: 0x42e4, 0x101d: 0x42e8, + 0x101e: 0x42ec, 0x101f: 0x2f5d, 0x1020: 0x42f0, 0x1021: 0x42f4, 0x1022: 0x42f8, 0x1023: 0x42fc, + 0x1024: 0x4300, 0x1025: 0x4305, 0x1026: 0x430a, 0x1027: 0x430f, 0x1028: 0x4313, 0x1029: 0x4317, + 0x102a: 0x431b, 0x102b: 0x431f, 0x102c: 0x4324, 0x102d: 0x4328, 0x102e: 0x432d, 0x102f: 0x4331, + 0x1030: 0x4335, 0x1031: 0x433a, 0x1032: 0x433f, 0x1033: 0x4343, 0x1034: 0x2b45, 0x1035: 0x4347, + 0x1036: 0x434b, 0x1037: 0x434f, 0x1038: 0x4353, 0x1039: 0x4357, 0x103a: 0x435b, 0x103b: 0x306a, + 0x103c: 0x435f, 0x103d: 0x4363, 0x103e: 0x4367, 0x103f: 0x436b, // Block 0x41, offset 0x1040 - 0x1060: 0x1693, 0x1061: 0x1699, 0x1062: 0x169f, 0x1063: 0x16a5, - 0x106a: 0x16ab, 0x106b: 0x16b1, 0x106c: 0x16b7, 0x106d: 0x16bd, - // Block 0x42, offset 0x1080 - 0x10a9: 0x16c3, - 0x10aa: 0x16c7, - // Block 0x43, offset 0x10c0 - 0x10e0: 0x001c, 0x10e1: 0x000d, 0x10e2: 0x000f, 0x10e3: 0x1488, - 0x10e4: 0x148a, 0x10e5: 0x148c, 0x10e6: 0x148e, 0x10e7: 0x1490, 0x10e8: 0x1492, 0x10e9: 0x16cb, - 0x10ea: 0x16ce, 0x10eb: 0x16d1, 0x10ec: 0x16d4, 0x10ed: 0x16d7, 0x10ee: 0x16da, 0x10ef: 0x16dd, - 0x10f0: 0x16e0, 0x10f1: 0x16e3, 0x10f2: 0x16e6, 0x10f3: 0x16e9, 0x10f4: 0x16ec, 0x10f5: 0x16f0, - 0x10f6: 0x16f4, 0x10f7: 0x16f8, 0x10f8: 0x16fc, 0x10f9: 0x1700, 0x10fa: 0x1704, 0x10fb: 0x1708, - 0x10fc: 0x170c, 0x10fd: 0x1710, 0x10fe: 0x1715, 0x10ff: 0x171a, - // Block 0x44, offset 0x1100 - 0x1100: 0x171f, 0x1101: 0x1724, 0x1102: 0x1729, 0x1103: 0x172e, 0x1104: 0x1733, 0x1105: 0x1738, - 0x1106: 0x173d, 0x1107: 0x1742, 0x1108: 0x1747, 0x1109: 0x174a, 0x110a: 0x174d, 0x110b: 0x1750, - 0x110c: 0x1753, 0x110d: 0x1756, 0x110e: 0x1759, 0x110f: 0x175c, 0x1110: 0x175f, 0x1111: 0x1762, - 0x1112: 0x1766, 0x1113: 0x176a, 0x1114: 0x176e, 0x1115: 0x1772, 0x1116: 0x1776, 0x1117: 0x177a, - 0x1118: 0x177e, 0x1119: 0x1782, 0x111a: 0x1786, 0x111b: 0x178a, 0x111c: 0x178e, 0x111d: 0x1792, - 0x111e: 0x1796, 0x111f: 0x179a, 0x1120: 0x179e, 0x1121: 0x17a2, 0x1122: 0x17a6, 0x1123: 0x17aa, - 0x1124: 0x17ae, 0x1125: 0x17b2, 0x1126: 0x17b6, 0x1127: 0x17ba, 0x1128: 0x17be, 0x1129: 0x17c2, - 0x112a: 0x17c6, 0x112b: 0x17ca, 0x112c: 0x17ce, 0x112d: 0x17d2, 0x112e: 0x17d6, 0x112f: 0x17da, - 0x1130: 0x17de, 0x1131: 0x17e2, 0x1132: 0x17e6, 0x1133: 0x17ea, 0x1134: 0x17ee, 0x1135: 0x17f2, - 0x1136: 0x0906, 0x1137: 0x090b, 0x1138: 0x14ad, 0x1139: 0x090d, 0x113a: 0x090f, 0x113b: 0x14d9, - 0x113c: 0x0914, 0x113d: 0x0916, 0x113e: 0x0918, 0x113f: 0x091a, - // Block 0x45, offset 0x1140 - 0x1140: 0x091c, 0x1141: 0x091e, 0x1142: 0x0920, 0x1143: 0x0922, 0x1144: 0x0924, 0x1145: 0x0929, - 0x1146: 0x14c8, 0x1147: 0x092b, 0x1148: 0x17f6, 0x1149: 0x092d, 0x114a: 0x092f, 0x114b: 0x155f, - 0x114c: 0x0931, 0x114d: 0x1570, 0x114e: 0x17f8, 0x114f: 0x14d4, 0x1150: 0x0007, 0x1151: 0x093d, - 0x1152: 0x0984, 0x1153: 0x093f, 0x1154: 0x0941, 0x1155: 0x098c, 0x1156: 0x094c, 0x1157: 0x0494, - 0x1158: 0x097c, 0x1159: 0x0499, 0x115a: 0x094e, 0x115b: 0x04c5, 0x115c: 0x0950, 0x115d: 0x14a0, - 0x115e: 0x001e, 0x115f: 0x0960, 0x1160: 0x17fa, 0x1161: 0x049b, 0x1162: 0x02c8, 0x1163: 0x0962, - 0x1164: 0x0964, 0x1165: 0x096d, 0x1166: 0x04a6, 0x1167: 0x04c7, 0x1168: 0x04a8, 0x1169: 0x09df, - 0x116a: 0x1486, - // Block 0x46, offset 0x1180 - 0x118c: 0x17fc, - // Block 0x47, offset 0x11c0 - 0x11f4: 0x1809, 0x11f5: 0x180d, - 0x11f6: 0x1810, - // Block 0x48, offset 0x1200 - 0x121c: 0x1814, - // Block 0x49, offset 0x1240 - 0x127c: 0x0499, 0x127d: 0x155f, - // Block 0x4a, offset 0x1280 - 0x12af: 0x181a, - // Block 0x4b, offset 0x12c0 - 0x12df: 0x181e, - // Block 0x4c, offset 0x1300 - 0x1333: 0x1822, - // Block 0x4d, offset 0x1340 - 0x1340: 0x1826, 0x1341: 0x182a, 0x1342: 0x182e, 0x1343: 0x1832, 0x1344: 0x1836, 0x1345: 0x183a, - 0x1346: 0x183e, 0x1347: 0x1842, 0x1348: 0x1846, 0x1349: 0x184a, 0x134a: 0x184e, 0x134b: 0x1852, - 0x134c: 0x1856, 0x134d: 0x185a, 0x134e: 0x185e, 0x134f: 0x1862, 0x1350: 0x1866, 0x1351: 0x186a, - 0x1352: 0x186e, 0x1353: 0x1872, 0x1354: 0x1876, 0x1355: 0x187a, 0x1356: 0x187e, 0x1357: 0x1882, - 0x1358: 0x1886, 0x1359: 0x188a, 0x135a: 0x188e, 0x135b: 0x1892, 0x135c: 0x1896, 0x135d: 0x189a, - 0x135e: 0x189e, 0x135f: 0x18a2, 0x1360: 0x18a6, 0x1361: 0x18aa, 0x1362: 0x18ae, 0x1363: 0x18b2, - 0x1364: 0x18b6, 0x1365: 0x18ba, 0x1366: 0x18be, 0x1367: 0x18c2, 0x1368: 0x18c6, 0x1369: 0x18ca, - 0x136a: 0x18ce, 0x136b: 0x18d2, 0x136c: 0x18d6, 0x136d: 0x18da, 0x136e: 0x18de, 0x136f: 0x18e2, - 0x1370: 0x18e6, 0x1371: 0x18ea, 0x1372: 0x18ee, 0x1373: 0x18f2, 0x1374: 0x18f6, 0x1375: 0x18fa, - 0x1376: 0x18fe, 0x1377: 0x1902, 0x1378: 0x1906, 0x1379: 0x190a, 0x137a: 0x190e, 0x137b: 0x1912, - 0x137c: 0x1916, 0x137d: 0x191a, 0x137e: 0x191e, 0x137f: 0x1922, - // Block 0x4e, offset 0x1380 - 0x1380: 0x1926, 0x1381: 0x192a, 0x1382: 0x192e, 0x1383: 0x1932, 0x1384: 0x1936, 0x1385: 0x193a, - 0x1386: 0x193e, 0x1387: 0x1942, 0x1388: 0x1946, 0x1389: 0x194a, 0x138a: 0x194e, 0x138b: 0x1952, - 0x138c: 0x1956, 0x138d: 0x195a, 0x138e: 0x195e, 0x138f: 0x1962, 0x1390: 0x1966, 0x1391: 0x196a, - 0x1392: 0x196e, 0x1393: 0x1972, 0x1394: 0x1976, 0x1395: 0x197a, 0x1396: 0x197e, 0x1397: 0x1982, - 0x1398: 0x1986, 0x1399: 0x198a, 0x139a: 0x198e, 0x139b: 0x1992, 0x139c: 0x1996, 0x139d: 0x199a, - 0x139e: 0x199e, 0x139f: 0x19a2, 0x13a0: 0x19a6, 0x13a1: 0x19aa, 0x13a2: 0x19ae, 0x13a3: 0x19b2, - 0x13a4: 0x19b6, 0x13a5: 0x19ba, 0x13a6: 0x19be, 0x13a7: 0x19c2, 0x13a8: 0x19c6, 0x13a9: 0x19ca, - 0x13aa: 0x19ce, 0x13ab: 0x19d2, 0x13ac: 0x19d6, 0x13ad: 0x19da, 0x13ae: 0x19de, 0x13af: 0x19e2, - 0x13b0: 0x19e6, 0x13b1: 0x19ea, 0x13b2: 0x19ee, 0x13b3: 0x19f2, 0x13b4: 0x19f6, 0x13b5: 0x19fa, - 0x13b6: 0x19fe, 0x13b7: 0x1a02, 0x13b8: 0x1a06, 0x13b9: 0x1a0a, 0x13ba: 0x1a0e, 0x13bb: 0x1a12, - 0x13bc: 0x1a16, 0x13bd: 0x1a1a, 0x13be: 0x1a1e, 0x13bf: 0x1a22, - // Block 0x4f, offset 0x13c0 - 0x13c0: 0x1a26, 0x13c1: 0x1a2a, 0x13c2: 0x1a2e, 0x13c3: 0x1a32, 0x13c4: 0x1a36, 0x13c5: 0x1a3a, - 0x13c6: 0x1a3e, 0x13c7: 0x1a42, 0x13c8: 0x1a46, 0x13c9: 0x1a4a, 0x13ca: 0x1a4e, 0x13cb: 0x1a52, - 0x13cc: 0x1a56, 0x13cd: 0x1a5a, 0x13ce: 0x1a5e, 0x13cf: 0x1a62, 0x13d0: 0x1a66, 0x13d1: 0x1a6a, - 0x13d2: 0x1a6e, 0x13d3: 0x1a72, 0x13d4: 0x1a76, 0x13d5: 0x1a7a, 0x13d6: 0x1a7e, 0x13d7: 0x1a82, - 0x13d8: 0x1a86, 0x13d9: 0x1a8a, 0x13da: 0x1a8e, 0x13db: 0x1a92, 0x13dc: 0x1a96, 0x13dd: 0x1a9a, - 0x13de: 0x1a9e, 0x13df: 0x1aa2, 0x13e0: 0x1aa6, 0x13e1: 0x1aaa, 0x13e2: 0x1aae, 0x13e3: 0x1ab2, - 0x13e4: 0x1ab6, 0x13e5: 0x1aba, 0x13e6: 0x1abe, 0x13e7: 0x1ac2, 0x13e8: 0x1ac6, 0x13e9: 0x1aca, - 0x13ea: 0x1ace, 0x13eb: 0x1ad2, 0x13ec: 0x1ad6, 0x13ed: 0x1ada, 0x13ee: 0x1ade, 0x13ef: 0x1ae2, - 0x13f0: 0x1ae6, 0x13f1: 0x1aea, 0x13f2: 0x1aee, 0x13f3: 0x1af2, 0x13f4: 0x1af6, 0x13f5: 0x1afa, - 0x13f6: 0x1afe, 0x13f7: 0x1b02, 0x13f8: 0x1b06, 0x13f9: 0x1b0a, 0x13fa: 0x1b0e, 0x13fb: 0x1b12, - 0x13fc: 0x1b16, 0x13fd: 0x1b1a, 0x13fe: 0x1b1e, 0x13ff: 0x1b22, - // Block 0x50, offset 0x1400 - 0x1400: 0x1b26, 0x1401: 0x1b2a, 0x1402: 0x1b2e, 0x1403: 0x1b32, 0x1404: 0x1b36, 0x1405: 0x1b3a, - 0x1406: 0x1b3e, 0x1407: 0x1b42, 0x1408: 0x1b46, 0x1409: 0x1b4a, 0x140a: 0x1b4e, 0x140b: 0x1b52, - 0x140c: 0x1b56, 0x140d: 0x1b5a, 0x140e: 0x1b5e, 0x140f: 0x1b62, 0x1410: 0x1b66, 0x1411: 0x1b6a, - 0x1412: 0x1b6e, 0x1413: 0x1b72, 0x1414: 0x1b76, 0x1415: 0x1b7a, - // Block 0x51, offset 0x1440 - 0x1440: 0x0001, - 0x1476: 0x1b7e, 0x1478: 0x1882, 0x1479: 0x1b82, 0x147a: 0x1b86, - // Block 0x52, offset 0x1480 - 0x148c: 0x1b8a, 0x148e: 0x1b91, 0x1490: 0x1b98, - 0x1492: 0x1b9f, 0x1494: 0x1ba6, 0x1496: 0x1bad, - 0x1498: 0x1bb4, 0x149a: 0x1bbb, 0x149c: 0x1bc2, - 0x149e: 0x1bc9, 0x14a0: 0x1bd0, 0x14a2: 0x1bd7, - 0x14a5: 0x1bde, 0x14a7: 0x1be5, 0x14a9: 0x1bec, - 0x14b0: 0x1bf3, 0x14b1: 0x1bfa, 0x14b3: 0x1c01, 0x14b4: 0x1c08, - 0x14b6: 0x1c0f, 0x14b7: 0x1c16, 0x14b9: 0x1c1d, 0x14ba: 0x1c24, - 0x14bc: 0x1c2b, 0x14bd: 0x1c32, - // Block 0x53, offset 0x14c0 - 0x14d4: 0x1c39, - 0x14db: 0x1c40, 0x14dc: 0x1c45, - 0x14de: 0x1c4a, 0x14df: 0x1c51, - 0x14ec: 0x1c58, 0x14ee: 0x1c5f, - 0x14f0: 0x1c66, 0x14f2: 0x1c6d, 0x14f4: 0x1c74, - 0x14f6: 0x1c7b, 0x14f8: 0x1c82, 0x14fa: 0x1c89, - 0x14fc: 0x1c90, 0x14fe: 0x1c97, - // Block 0x54, offset 0x1500 - 0x1500: 0x1c9e, 0x1502: 0x1ca5, 0x1505: 0x1cac, - 0x1507: 0x1cb3, 0x1509: 0x1cba, - 0x1510: 0x1cc1, 0x1511: 0x1cc8, - 0x1513: 0x1ccf, 0x1514: 0x1cd6, 0x1516: 0x1cdd, 0x1517: 0x1ce4, - 0x1519: 0x1ceb, 0x151a: 0x1cf2, 0x151c: 0x1cf9, 0x151d: 0x1d00, - 0x1534: 0x1d07, - 0x1537: 0x1d0e, 0x1538: 0x1d15, 0x1539: 0x1d1c, 0x153a: 0x1d23, - 0x153e: 0x1d2a, 0x153f: 0x1d31, - // Block 0x55, offset 0x1540 - 0x1571: 0x1d38, 0x1572: 0x1d3c, 0x1573: 0x1d40, 0x1574: 0x1d44, 0x1575: 0x1d48, - 0x1576: 0x1d4c, 0x1577: 0x1d50, 0x1578: 0x1d54, 0x1579: 0x1d58, 0x157a: 0x1d5c, 0x157b: 0x1d60, - 0x157c: 0x1d64, 0x157d: 0x1d68, 0x157e: 0x1d6c, 0x157f: 0x1d70, - // Block 0x56, offset 0x1580 - 0x1580: 0x1d74, 0x1581: 0x1d78, 0x1582: 0x1d7c, 0x1583: 0x1d80, 0x1584: 0x1d84, 0x1585: 0x1d88, - 0x1586: 0x1d8c, 0x1587: 0x1d90, 0x1588: 0x1d94, 0x1589: 0x1d98, 0x158a: 0x1d9c, 0x158b: 0x1da0, - 0x158c: 0x1da4, 0x158d: 0x1da8, 0x158e: 0x1dac, 0x158f: 0x1db0, 0x1590: 0x1db4, 0x1591: 0x1db8, - 0x1592: 0x1dbc, 0x1593: 0x1dc0, 0x1594: 0x1dc4, 0x1595: 0x1dc8, 0x1596: 0x1dcc, 0x1597: 0x1dd0, - 0x1598: 0x1dd4, 0x1599: 0x1dd8, 0x159a: 0x1ddc, 0x159b: 0x1de0, 0x159c: 0x1de4, 0x159d: 0x1de8, - 0x159e: 0x1dec, 0x159f: 0x1df0, 0x15a0: 0x1df4, 0x15a1: 0x1df8, 0x15a2: 0x1dfc, 0x15a3: 0x1e00, - 0x15a4: 0x1e04, 0x15a5: 0x1e08, 0x15a6: 0x1e0c, 0x15a7: 0x1e10, 0x15a8: 0x1e14, 0x15a9: 0x1e18, - 0x15aa: 0x1e1c, 0x15ab: 0x1e20, 0x15ac: 0x1e24, 0x15ad: 0x1e28, 0x15ae: 0x1e2c, 0x15af: 0x1e30, - 0x15b0: 0x1e34, 0x15b1: 0x1e38, 0x15b2: 0x1e3c, 0x15b3: 0x1e40, 0x15b4: 0x1e44, 0x15b5: 0x1e48, - 0x15b6: 0x1e4c, 0x15b7: 0x1e50, 0x15b8: 0x1e54, 0x15b9: 0x1e58, 0x15ba: 0x1e5c, 0x15bb: 0x1e60, - 0x15bc: 0x1e64, 0x15bd: 0x1e68, 0x15be: 0x1e6c, 0x15bf: 0x1e70, - // Block 0x57, offset 0x15c0 - 0x15c0: 0x1e74, 0x15c1: 0x1e78, 0x15c2: 0x1e7c, 0x15c3: 0x1e80, 0x15c4: 0x1e84, 0x15c5: 0x1e88, - 0x15c6: 0x1e8c, 0x15c7: 0x1e90, 0x15c8: 0x1e94, 0x15c9: 0x1e98, 0x15ca: 0x1e9c, 0x15cb: 0x1ea0, - 0x15cc: 0x1ea4, 0x15cd: 0x1ea8, 0x15ce: 0x1eac, - 0x15d2: 0x1826, 0x15d3: 0x183e, 0x15d4: 0x1eb0, 0x15d5: 0x1eb4, 0x15d6: 0x1eb8, 0x15d7: 0x1ebc, - 0x15d8: 0x1ec0, 0x15d9: 0x1ec4, 0x15da: 0x1836, 0x15db: 0x1ec8, 0x15dc: 0x1ecc, 0x15dd: 0x1ed0, - 0x15de: 0x1ed4, 0x15df: 0x1846, - // Block 0x58, offset 0x1600 - 0x1600: 0x1ed8, 0x1601: 0x1ede, 0x1602: 0x1ee4, 0x1603: 0x1eea, 0x1604: 0x1ef0, 0x1605: 0x1ef6, - 0x1606: 0x1efc, 0x1607: 0x1f02, 0x1608: 0x1f08, 0x1609: 0x1f0e, 0x160a: 0x1f14, 0x160b: 0x1f1a, - 0x160c: 0x1f20, 0x160d: 0x1f26, 0x160e: 0x1f2c, 0x160f: 0x1f35, 0x1610: 0x1f3e, 0x1611: 0x1f47, - 0x1612: 0x1f50, 0x1613: 0x1f59, 0x1614: 0x1f62, 0x1615: 0x1f6b, 0x1616: 0x1f74, 0x1617: 0x1f7d, - 0x1618: 0x1f86, 0x1619: 0x1f8f, 0x161a: 0x1f98, 0x161b: 0x1fa1, 0x161c: 0x1faa, 0x161d: 0x1fb3, - 0x161e: 0x1fc5, 0x1620: 0x1fd4, 0x1621: 0x1fda, 0x1622: 0x1fe0, 0x1623: 0x1fe6, - 0x1624: 0x1fec, 0x1625: 0x1ff2, 0x1626: 0x1ff8, 0x1627: 0x1ffe, 0x1628: 0x2004, 0x1629: 0x200a, - 0x162a: 0x2010, 0x162b: 0x2016, 0x162c: 0x201c, 0x162d: 0x2022, 0x162e: 0x2028, 0x162f: 0x202e, - 0x1630: 0x2034, 0x1631: 0x203a, 0x1632: 0x2040, 0x1633: 0x2046, 0x1634: 0x204c, 0x1635: 0x2052, - 0x1636: 0x2058, 0x1637: 0x205e, 0x1638: 0x2064, 0x1639: 0x206a, 0x163a: 0x2070, 0x163b: 0x2076, - 0x163c: 0x207c, 0x163d: 0x2082, 0x163e: 0x2088, 0x163f: 0x208e, - // Block 0x59, offset 0x1640 - 0x1640: 0x2094, 0x1641: 0x209a, 0x1642: 0x20a0, 0x1643: 0x20a6, 0x1644: 0x20ac, 0x1645: 0x20b0, - 0x1646: 0x192e, 0x1647: 0x20b4, - 0x1650: 0x20b8, 0x1651: 0x20bc, - 0x1652: 0x20bf, 0x1653: 0x20c2, 0x1654: 0x20c5, 0x1655: 0x20c8, 0x1656: 0x20cb, 0x1657: 0x20ce, - 0x1658: 0x20d1, 0x1659: 0x20d4, 0x165a: 0x20d7, 0x165b: 0x20da, 0x165c: 0x20dd, 0x165d: 0x20e0, - 0x165e: 0x20e3, 0x165f: 0x20e6, 0x1660: 0x1d38, 0x1661: 0x1d44, 0x1662: 0x1d50, 0x1663: 0x1d58, - 0x1664: 0x1d78, 0x1665: 0x1d7c, 0x1666: 0x1d88, 0x1667: 0x1d90, 0x1668: 0x1d94, 0x1669: 0x1d9c, - 0x166a: 0x1da0, 0x166b: 0x1da4, 0x166c: 0x1da8, 0x166d: 0x1dac, 0x166e: 0x20e9, 0x166f: 0x20f0, - 0x1670: 0x20f7, 0x1671: 0x20fe, 0x1672: 0x2105, 0x1673: 0x210c, 0x1674: 0x2113, 0x1675: 0x211a, - 0x1676: 0x2121, 0x1677: 0x2128, 0x1678: 0x212f, 0x1679: 0x2136, 0x167a: 0x213d, 0x167b: 0x2144, - 0x167c: 0x214b, 0x167d: 0x215b, 0x167e: 0x2168, - // Block 0x5a, offset 0x1680 - 0x1680: 0x1826, 0x1681: 0x183e, 0x1682: 0x1eb0, 0x1683: 0x1eb4, 0x1684: 0x216f, 0x1685: 0x2173, - 0x1686: 0x2177, 0x1687: 0x1852, 0x1688: 0x217b, 0x1689: 0x1882, 0x168a: 0x194a, 0x168b: 0x197a, - 0x168c: 0x1976, 0x168d: 0x194e, 0x168e: 0x1abe, 0x168f: 0x18a2, 0x1690: 0x1942, 0x1691: 0x217f, - 0x1692: 0x2183, 0x1693: 0x2187, 0x1694: 0x218b, 0x1695: 0x218f, 0x1696: 0x2193, 0x1697: 0x2197, - 0x1698: 0x219b, 0x1699: 0x219f, 0x169a: 0x21a3, 0x169b: 0x18ba, 0x169c: 0x21a7, 0x169d: 0x21ab, - 0x169e: 0x21af, 0x169f: 0x21b3, 0x16a0: 0x21b7, 0x16a1: 0x21bb, 0x16a2: 0x21bf, 0x16a3: 0x21c3, - 0x16a4: 0x1eb8, 0x16a5: 0x1ebc, 0x16a6: 0x1ec0, 0x16a7: 0x21c7, 0x16a8: 0x21cb, 0x16a9: 0x21cf, - 0x16aa: 0x21d3, 0x16ab: 0x21d7, 0x16ac: 0x21db, 0x16ad: 0x21df, 0x16ae: 0x21e3, 0x16af: 0x21e7, - 0x16b0: 0x21eb, 0x16b1: 0x21ef, 0x16b2: 0x21f2, 0x16b3: 0x21f5, 0x16b4: 0x21f8, 0x16b5: 0x21fb, - 0x16b6: 0x21fe, 0x16b7: 0x2201, 0x16b8: 0x2204, 0x16b9: 0x2207, 0x16ba: 0x220a, 0x16bb: 0x220d, - 0x16bc: 0x2210, 0x16bd: 0x2213, 0x16be: 0x2216, 0x16bf: 0x2219, - // Block 0x5b, offset 0x16c0 - 0x16c0: 0x221c, 0x16c1: 0x2221, 0x16c2: 0x2226, 0x16c3: 0x222b, 0x16c4: 0x2230, 0x16c5: 0x2235, - 0x16c6: 0x223a, 0x16c7: 0x223f, 0x16c8: 0x2244, 0x16c9: 0x2249, 0x16ca: 0x224f, 0x16cb: 0x2255, - 0x16cc: 0x225b, 0x16cd: 0x225e, 0x16ce: 0x2262, 0x16cf: 0x2265, 0x16d0: 0x2269, 0x16d1: 0x226d, - 0x16d2: 0x2271, 0x16d3: 0x2275, 0x16d4: 0x2279, 0x16d5: 0x227d, 0x16d6: 0x2281, 0x16d7: 0x2285, - 0x16d8: 0x2289, 0x16d9: 0x228d, 0x16da: 0x2291, 0x16db: 0x2295, 0x16dc: 0x2299, 0x16dd: 0x229d, - 0x16de: 0x22a1, 0x16df: 0x22a5, 0x16e0: 0x22a9, 0x16e1: 0x22ad, 0x16e2: 0x22b1, 0x16e3: 0x22b5, - 0x16e4: 0x22b9, 0x16e5: 0x22bd, 0x16e6: 0x22c1, 0x16e7: 0x22c5, 0x16e8: 0x22c9, 0x16e9: 0x22cd, - 0x16ea: 0x22d1, 0x16eb: 0x22d5, 0x16ec: 0x22d9, 0x16ed: 0x22dd, 0x16ee: 0x22e1, 0x16ef: 0x22e5, - 0x16f0: 0x22e9, 0x16f1: 0x22ed, 0x16f2: 0x22f1, 0x16f3: 0x22f5, 0x16f4: 0x22f9, 0x16f5: 0x22fd, - 0x16f6: 0x2301, 0x16f7: 0x2305, 0x16f8: 0x2309, 0x16f9: 0x230d, 0x16fa: 0x2311, 0x16fb: 0x2315, - 0x16fc: 0x2319, 0x16fd: 0x231d, 0x16fe: 0x2321, - // Block 0x5c, offset 0x1700 - 0x1700: 0x2325, 0x1701: 0x2335, 0x1702: 0x2342, 0x1703: 0x2352, 0x1704: 0x235c, 0x1705: 0x236c, - 0x1706: 0x2376, 0x1707: 0x2380, 0x1708: 0x2393, 0x1709: 0x23a0, 0x170a: 0x23aa, 0x170b: 0x23b4, - 0x170c: 0x23be, 0x170d: 0x23cb, 0x170e: 0x23d8, 0x170f: 0x23e5, 0x1710: 0x23f2, 0x1711: 0x23ff, - 0x1712: 0x240c, 0x1713: 0x2419, 0x1714: 0x242c, 0x1715: 0x2433, 0x1716: 0x2446, 0x1717: 0x2459, - 0x1718: 0x2469, 0x1719: 0x2476, 0x171a: 0x2489, 0x171b: 0x249c, 0x171c: 0x24a9, 0x171d: 0x24b3, - 0x171e: 0x24bd, 0x171f: 0x24ca, 0x1720: 0x24d7, 0x1721: 0x24e7, 0x1722: 0x24f7, 0x1723: 0x2501, - 0x1724: 0x250b, 0x1725: 0x2518, 0x1726: 0x2522, 0x1727: 0x252c, 0x1728: 0x2533, 0x1729: 0x253a, - 0x172a: 0x2544, 0x172b: 0x254e, 0x172c: 0x2561, 0x172d: 0x256e, 0x172e: 0x257e, 0x172f: 0x2591, - 0x1730: 0x259e, 0x1731: 0x25a8, 0x1732: 0x25b2, 0x1733: 0x25c5, 0x1734: 0x25d2, 0x1735: 0x25e5, - 0x1736: 0x25ef, 0x1737: 0x25ff, 0x1738: 0x2609, 0x1739: 0x2616, 0x173a: 0x2620, 0x173b: 0x262d, - 0x173c: 0x263d, 0x173d: 0x264a, 0x173e: 0x265a, 0x173f: 0x2667, - // Block 0x5d, offset 0x1740 - 0x1740: 0x266e, 0x1741: 0x267e, 0x1742: 0x2688, 0x1743: 0x2692, 0x1744: 0x269f, 0x1745: 0x26a9, - 0x1746: 0x26b3, 0x1747: 0x26bd, 0x1748: 0x26cd, 0x1749: 0x26da, 0x174a: 0x26e1, 0x174b: 0x26f4, - 0x174c: 0x26fe, 0x174d: 0x270e, 0x174e: 0x271b, 0x174f: 0x2728, 0x1750: 0x2732, 0x1751: 0x273c, - 0x1752: 0x2749, 0x1753: 0x2750, 0x1754: 0x275d, 0x1755: 0x276d, 0x1756: 0x2774, 0x1757: 0x2787, - 0x1758: 0x2791, 0x1759: 0x2796, 0x175a: 0x279b, 0x175b: 0x27a0, 0x175c: 0x27a5, 0x175d: 0x27aa, - 0x175e: 0x27af, 0x175f: 0x27b4, 0x1760: 0x27b9, 0x1761: 0x27be, 0x1762: 0x27c3, 0x1763: 0x27c9, - 0x1764: 0x27cf, 0x1765: 0x27d5, 0x1766: 0x27db, 0x1767: 0x27e1, 0x1768: 0x27e7, 0x1769: 0x27ed, - 0x176a: 0x27f3, 0x176b: 0x27f9, 0x176c: 0x27ff, 0x176d: 0x2805, 0x176e: 0x280b, 0x176f: 0x2811, - 0x1770: 0x2817, 0x1771: 0x281d, 0x1772: 0x2821, 0x1773: 0x2824, 0x1774: 0x2827, 0x1775: 0x282b, - 0x1776: 0x282e, 0x1777: 0x2831, 0x1778: 0x2834, 0x1779: 0x2838, 0x177a: 0x283c, 0x177b: 0x283f, - 0x177c: 0x2846, 0x177d: 0x284d, 0x177e: 0x2854, 0x177f: 0x285b, - // Block 0x5e, offset 0x1780 - 0x1780: 0x2868, 0x1781: 0x286b, 0x1782: 0x286e, 0x1783: 0x2872, 0x1784: 0x2875, 0x1785: 0x2878, - 0x1786: 0x287b, 0x1787: 0x287e, 0x1788: 0x2881, 0x1789: 0x2885, 0x178a: 0x288a, 0x178b: 0x288d, - 0x178c: 0x2890, 0x178d: 0x2894, 0x178e: 0x2898, 0x178f: 0x289b, 0x1790: 0x289e, 0x1791: 0x28a1, - 0x1792: 0x28a5, 0x1793: 0x28a9, 0x1794: 0x28ad, 0x1795: 0x28b1, 0x1796: 0x28b5, 0x1797: 0x28b8, - 0x1798: 0x28bb, 0x1799: 0x28be, 0x179a: 0x28c1, 0x179b: 0x28c4, 0x179c: 0x28c8, 0x179d: 0x28cb, - 0x179e: 0x28ce, 0x179f: 0x28d1, 0x17a0: 0x28d5, 0x17a1: 0x28d9, 0x17a2: 0x28dc, 0x17a3: 0x28e0, - 0x17a4: 0x28e4, 0x17a5: 0x28e8, 0x17a6: 0x28eb, 0x17a7: 0x28ef, 0x17a8: 0x28f5, 0x17a9: 0x28fc, - 0x17aa: 0x28ff, 0x17ab: 0x2903, 0x17ac: 0x2907, 0x17ad: 0x290b, 0x17ae: 0x290f, 0x17af: 0x2917, - 0x17b0: 0x2920, 0x17b1: 0x2923, 0x17b2: 0x2926, 0x17b3: 0x292a, 0x17b4: 0x292d, 0x17b5: 0x2930, - 0x17b6: 0x2933, 0x17b7: 0x2937, 0x17b8: 0x293a, 0x17b9: 0x293d, 0x17ba: 0x2940, 0x17bb: 0x2943, - 0x17bc: 0x2946, 0x17bd: 0x294a, 0x17be: 0x294d, 0x17bf: 0x2950, - // Block 0x5f, offset 0x17c0 - 0x17c0: 0x2953, 0x17c1: 0x2957, 0x17c2: 0x295b, 0x17c3: 0x2960, 0x17c4: 0x2963, 0x17c5: 0x2966, - 0x17c6: 0x2969, 0x17c7: 0x2970, 0x17c8: 0x2974, 0x17c9: 0x2977, 0x17ca: 0x297a, 0x17cb: 0x297d, - 0x17cc: 0x2980, 0x17cd: 0x2983, 0x17ce: 0x2986, 0x17cf: 0x2989, 0x17d0: 0x298c, 0x17d1: 0x298f, - 0x17d2: 0x2992, 0x17d3: 0x2996, 0x17d4: 0x2999, 0x17d5: 0x299c, 0x17d6: 0x29a0, 0x17d7: 0x29a4, - 0x17d8: 0x29a7, 0x17d9: 0x29ac, 0x17da: 0x29b0, 0x17db: 0x29b3, 0x17dc: 0x29b6, 0x17dd: 0x29b9, - 0x17de: 0x29bc, 0x17df: 0x29c2, 0x17e0: 0x29c8, 0x17e1: 0x29cd, 0x17e2: 0x29d2, 0x17e3: 0x29d7, - 0x17e4: 0x29dc, 0x17e5: 0x29e1, 0x17e6: 0x29e6, 0x17e7: 0x29eb, 0x17e8: 0x29f0, 0x17e9: 0x29f5, - 0x17ea: 0x29fb, 0x17eb: 0x2a01, 0x17ec: 0x2a07, 0x17ed: 0x2a0d, 0x17ee: 0x2a13, 0x17ef: 0x2a19, - 0x17f0: 0x2a1f, 0x17f1: 0x2a25, 0x17f2: 0x2a2b, 0x17f3: 0x2a31, 0x17f4: 0x2a37, 0x17f5: 0x2a3d, - 0x17f6: 0x2a43, 0x17f7: 0x2a49, 0x17f8: 0x2a4f, 0x17f9: 0x2a55, 0x17fa: 0x2a5b, 0x17fb: 0x2a61, - 0x17fc: 0x2a67, 0x17fd: 0x2a6d, 0x17fe: 0x2a73, 0x17ff: 0x2a79, - // Block 0x60, offset 0x1800 - 0x1830: 0x2a7d, - // Block 0x61, offset 0x1840 - 0x1840: 0x2a81, 0x1841: 0x2a85, 0x1842: 0x1a9e, 0x1843: 0x2a89, 0x1844: 0x2a8d, 0x1845: 0x2a91, - 0x1846: 0x2a95, 0x1847: 0x1b76, 0x1848: 0x1b76, 0x1849: 0x2a99, 0x184a: 0x1abe, 0x184b: 0x2a9d, - 0x184c: 0x2aa1, 0x184d: 0x2aa5, 0x184e: 0x2aa9, 0x184f: 0x2aad, 0x1850: 0x2ab1, 0x1851: 0x2ab5, - 0x1852: 0x2ab9, 0x1853: 0x2abd, 0x1854: 0x2ac1, 0x1855: 0x2ac5, 0x1856: 0x2ac9, 0x1857: 0x2acd, - 0x1858: 0x2ad1, 0x1859: 0x2ad5, 0x185a: 0x2ad9, 0x185b: 0x2add, 0x185c: 0x2ae1, 0x185d: 0x2ae5, - 0x185e: 0x2ae9, 0x185f: 0x2aed, 0x1860: 0x2af1, 0x1861: 0x2af5, 0x1862: 0x2af9, 0x1863: 0x2afd, - 0x1864: 0x2b01, 0x1865: 0x2b05, 0x1866: 0x2b09, 0x1867: 0x2b0d, 0x1868: 0x2b11, 0x1869: 0x2b15, - 0x186a: 0x2b19, 0x186b: 0x2b1d, 0x186c: 0x2b21, 0x186d: 0x2b25, 0x186e: 0x2b29, 0x186f: 0x2b2d, - 0x1870: 0x2b31, 0x1871: 0x2b35, 0x1872: 0x2b39, 0x1873: 0x2b3d, 0x1874: 0x1a16, 0x1875: 0x2b41, - 0x1876: 0x2b45, 0x1877: 0x2b49, 0x1878: 0x2b4d, 0x1879: 0x2b51, 0x187a: 0x2b55, 0x187b: 0x2b59, - 0x187c: 0x2b5d, 0x187d: 0x2b61, 0x187e: 0x2b65, 0x187f: 0x2b69, - // Block 0x62, offset 0x1880 - 0x1880: 0x1b3a, 0x1881: 0x2b6d, 0x1882: 0x2b71, 0x1883: 0x2b75, 0x1884: 0x2b79, 0x1885: 0x2b7d, - 0x1886: 0x2b81, 0x1887: 0x2b85, 0x1888: 0x2b89, 0x1889: 0x2b8d, 0x188a: 0x2b91, 0x188b: 0x2b95, - 0x188c: 0x2b99, 0x188d: 0x2b9d, 0x188e: 0x2ba1, 0x188f: 0x2ba5, 0x1890: 0x2ba9, 0x1891: 0x2bad, - 0x1892: 0x2bb1, 0x1893: 0x2bb5, 0x1894: 0x2bb9, 0x1895: 0x2bbd, 0x1896: 0x2bc1, 0x1897: 0x2bc5, - 0x1898: 0x2bc9, 0x1899: 0x2bcd, 0x189a: 0x2bd1, 0x189b: 0x2bd5, 0x189c: 0x2ac1, 0x189d: 0x2bd9, - 0x189e: 0x2bdd, 0x189f: 0x2be1, 0x18a0: 0x2be5, 0x18a1: 0x2be9, 0x18a2: 0x2bed, 0x18a3: 0x2bf1, - 0x18a4: 0x2bf5, 0x18a5: 0x2bf9, 0x18a6: 0x2bfd, 0x18a7: 0x2c01, 0x18a8: 0x2c05, 0x18a9: 0x2c09, - 0x18aa: 0x2c0d, 0x18ab: 0x2c11, 0x18ac: 0x2c15, 0x18ad: 0x2c19, 0x18ae: 0x2c1d, 0x18af: 0x2c21, - 0x18b0: 0x2c25, 0x18b1: 0x1aa6, 0x18b2: 0x2c29, 0x18b3: 0x2c2d, 0x18b4: 0x2c31, 0x18b5: 0x2c35, - 0x18b6: 0x2c39, 0x18b7: 0x2c3d, 0x18b8: 0x2c41, 0x18b9: 0x2c45, 0x18ba: 0x2c49, 0x18bb: 0x2c4d, - 0x18bc: 0x2c51, 0x18bd: 0x2c55, 0x18be: 0x2c59, 0x18bf: 0x2c5d, - // Block 0x63, offset 0x18c0 - 0x18c0: 0x2c61, 0x18c1: 0x18ba, 0x18c2: 0x2c65, 0x18c3: 0x2c69, 0x18c4: 0x2c6d, 0x18c5: 0x2c71, - 0x18c6: 0x2c75, 0x18c7: 0x2c79, 0x18c8: 0x2c7d, 0x18c9: 0x2c81, 0x18ca: 0x186e, 0x18cb: 0x2c85, - 0x18cc: 0x2c89, 0x18cd: 0x2c8d, 0x18ce: 0x2c91, 0x18cf: 0x2c95, 0x18d0: 0x2c99, 0x18d1: 0x2c9d, - 0x18d2: 0x2ca1, 0x18d3: 0x2ca5, 0x18d4: 0x2ca9, 0x18d5: 0x2cad, 0x18d6: 0x2cb1, 0x18d7: 0x2cb5, - 0x18d8: 0x2cb9, 0x18d9: 0x2cbd, 0x18da: 0x2cc1, 0x18db: 0x2cc5, 0x18dc: 0x2cc9, 0x18dd: 0x2ccd, - 0x18de: 0x2cd1, 0x18df: 0x2cd5, 0x18e0: 0x2cd9, 0x18e1: 0x2c21, 0x18e2: 0x2cdd, 0x18e3: 0x2ce1, - 0x18e4: 0x2ce5, 0x18e5: 0x2ce9, 0x18e6: 0x2ced, 0x18e7: 0x2cf1, 0x18e8: 0x2cf5, 0x18e9: 0x2cf9, - 0x18ea: 0x2be1, 0x18eb: 0x2cfd, 0x18ec: 0x2d01, 0x18ed: 0x2d05, 0x18ee: 0x2d09, 0x18ef: 0x2d0d, - 0x18f0: 0x2d11, 0x18f1: 0x2d15, 0x18f2: 0x2d19, 0x18f3: 0x2d1d, 0x18f4: 0x2d21, 0x18f5: 0x2d25, - 0x18f6: 0x2d29, 0x18f7: 0x2d2d, 0x18f8: 0x2d31, 0x18f9: 0x2d35, 0x18fa: 0x2d39, 0x18fb: 0x2d3d, - 0x18fc: 0x2d41, 0x18fd: 0x2d45, 0x18fe: 0x2d49, 0x18ff: 0x2ac1, - // Block 0x64, offset 0x1900 - 0x1900: 0x2d4d, 0x1901: 0x2d51, 0x1902: 0x2d55, 0x1903: 0x2d59, 0x1904: 0x1b72, 0x1905: 0x2d5d, - 0x1906: 0x2d61, 0x1907: 0x2d65, 0x1908: 0x2d69, 0x1909: 0x2d6d, 0x190a: 0x2d71, 0x190b: 0x2d75, - 0x190c: 0x2d79, 0x190d: 0x2d7d, 0x190e: 0x2d81, 0x190f: 0x2d85, 0x1910: 0x2d89, 0x1911: 0x2173, - 0x1912: 0x2d8d, 0x1913: 0x2d91, 0x1914: 0x2d95, 0x1915: 0x2d99, 0x1916: 0x2d9d, 0x1917: 0x2da1, - 0x1918: 0x2da5, 0x1919: 0x2da9, 0x191a: 0x2dad, 0x191b: 0x2be9, 0x191c: 0x2db1, 0x191d: 0x2db5, - 0x191e: 0x2db9, 0x191f: 0x2dbd, 0x1920: 0x2dc1, 0x1921: 0x2dc5, 0x1922: 0x2dc9, 0x1923: 0x2dcd, - 0x1924: 0x2dd1, 0x1925: 0x2dd5, 0x1926: 0x2dd9, 0x1927: 0x2ddd, 0x1928: 0x2de1, 0x1929: 0x1aba, - 0x192a: 0x2de5, 0x192b: 0x2de9, 0x192c: 0x2ded, 0x192d: 0x2df1, 0x192e: 0x2df5, 0x192f: 0x2df9, - 0x1930: 0x2dfd, 0x1931: 0x2e01, 0x1932: 0x2e05, 0x1933: 0x2e09, 0x1934: 0x2e0d, 0x1935: 0x2e11, - 0x1936: 0x2e15, 0x1937: 0x19f6, 0x1938: 0x2e19, 0x1939: 0x2e1d, 0x193a: 0x2e21, 0x193b: 0x2e25, - 0x193c: 0x2e29, 0x193d: 0x2e2d, 0x193e: 0x2e31, 0x193f: 0x2e35, - // Block 0x65, offset 0x1940 - 0x1940: 0x2e39, 0x1941: 0x2e3d, 0x1942: 0x2e41, 0x1943: 0x2e45, 0x1944: 0x2e49, 0x1945: 0x2e4d, - 0x1946: 0x2e51, 0x1947: 0x2e55, 0x1948: 0x1a62, 0x1949: 0x2e59, 0x194a: 0x1a6e, 0x194b: 0x2e5d, - 0x194c: 0x2e61, 0x194d: 0x2e65, 0x1950: 0x2e69, - 0x1952: 0x2e6d, 0x1955: 0x2e71, 0x1956: 0x2e75, 0x1957: 0x2e79, - 0x1958: 0x2e7d, 0x1959: 0x2e81, 0x195a: 0x2e85, 0x195b: 0x2e89, 0x195c: 0x2e8d, 0x195d: 0x2e91, - 0x195e: 0x1a12, 0x1960: 0x2e95, 0x1962: 0x2e99, - 0x1965: 0x2e9d, 0x1966: 0x2ea1, - 0x196a: 0x2ea5, 0x196b: 0x2ea9, 0x196c: 0x2ead, 0x196d: 0x2eb1, - 0x1970: 0x2eb5, 0x1971: 0x2eb9, 0x1972: 0x2ebd, 0x1973: 0x2ec1, 0x1974: 0x2ec5, 0x1975: 0x2ec9, - 0x1976: 0x2ecd, 0x1977: 0x2ed1, 0x1978: 0x2ed5, 0x1979: 0x2ed9, 0x197a: 0x2edd, 0x197b: 0x2ee1, - 0x197c: 0x18d6, 0x197d: 0x2ee5, 0x197e: 0x2ee9, 0x197f: 0x2eed, - // Block 0x66, offset 0x1980 - 0x1980: 0x2ef1, 0x1981: 0x2ef5, 0x1982: 0x2ef9, 0x1983: 0x2efd, 0x1984: 0x2f01, 0x1985: 0x2f05, - 0x1986: 0x2f09, 0x1987: 0x2f0d, 0x1988: 0x2f11, 0x1989: 0x2f15, 0x198a: 0x2f19, 0x198b: 0x2f1d, - 0x198c: 0x2187, 0x198d: 0x2f21, 0x198e: 0x2f25, 0x198f: 0x2f29, 0x1990: 0x2f2d, 0x1991: 0x2197, - 0x1992: 0x2f31, 0x1993: 0x2f35, 0x1994: 0x2f39, 0x1995: 0x2f3d, 0x1996: 0x2f41, 0x1997: 0x2cb1, - 0x1998: 0x2f45, 0x1999: 0x2f49, 0x199a: 0x2f4d, 0x199b: 0x2f51, 0x199c: 0x2f55, 0x199d: 0x2f59, - 0x199e: 0x2f59, 0x199f: 0x2f5d, 0x19a0: 0x2f61, 0x19a1: 0x2f65, 0x19a2: 0x2f69, 0x19a3: 0x2f6d, - 0x19a4: 0x2f71, 0x19a5: 0x2f75, 0x19a6: 0x2f79, 0x19a7: 0x2e9d, 0x19a8: 0x2f7d, 0x19a9: 0x2f81, - 0x19aa: 0x2f85, 0x19ab: 0x2f89, 0x19ac: 0x2f8d, 0x19ad: 0x2f92, - 0x19b0: 0x2f96, 0x19b1: 0x2f9a, 0x19b2: 0x2f9e, 0x19b3: 0x2fa2, 0x19b4: 0x2fa6, 0x19b5: 0x2faa, - 0x19b6: 0x2fae, 0x19b7: 0x2fb2, 0x19b8: 0x2ecd, 0x19b9: 0x2fb6, 0x19ba: 0x2fba, 0x19bb: 0x2fbe, - 0x19bc: 0x2e69, 0x19bd: 0x2fc2, 0x19be: 0x2fc6, 0x19bf: 0x2fca, - // Block 0x67, offset 0x19c0 - 0x19c0: 0x2fce, 0x19c1: 0x2fd2, 0x19c2: 0x2fd6, 0x19c3: 0x2fda, 0x19c4: 0x2fde, 0x19c5: 0x2fe2, - 0x19c6: 0x2fe6, 0x19c7: 0x2fea, 0x19c8: 0x2fee, 0x19c9: 0x2eed, 0x19ca: 0x2ff2, 0x19cb: 0x2ef1, - 0x19cc: 0x2ff6, 0x19cd: 0x2ffa, 0x19ce: 0x2ffe, 0x19cf: 0x3002, 0x19d0: 0x3006, 0x19d1: 0x2e6d, - 0x19d2: 0x2b15, 0x19d3: 0x300a, 0x19d4: 0x300e, 0x19d5: 0x195a, 0x19d6: 0x2c25, 0x19d7: 0x2d71, - 0x19d8: 0x3012, 0x19d9: 0x3016, 0x19da: 0x2f0d, 0x19db: 0x301a, 0x19dc: 0x2f11, 0x19dd: 0x301e, - 0x19de: 0x3022, 0x19df: 0x3026, 0x19e0: 0x2e75, 0x19e1: 0x302a, 0x19e2: 0x302e, 0x19e3: 0x3032, - 0x19e4: 0x3036, 0x19e5: 0x303a, 0x19e6: 0x2e79, 0x19e7: 0x303e, 0x19e8: 0x3042, 0x19e9: 0x3046, - 0x19ea: 0x304a, 0x19eb: 0x304e, 0x19ec: 0x3052, 0x19ed: 0x2f41, 0x19ee: 0x3056, 0x19ef: 0x305a, - 0x19f0: 0x2cb1, 0x19f1: 0x305e, 0x19f2: 0x2f51, 0x19f3: 0x3062, 0x19f4: 0x3066, 0x19f5: 0x306a, - 0x19f6: 0x306e, 0x19f7: 0x3072, 0x19f8: 0x2f65, 0x19f9: 0x3076, 0x19fa: 0x2e99, 0x19fb: 0x307a, - 0x19fc: 0x2f69, 0x19fd: 0x2bd9, 0x19fe: 0x307e, 0x19ff: 0x2f6d, - // Block 0x68, offset 0x1a00 - 0x1a00: 0x3082, 0x1a01: 0x2f75, 0x1a02: 0x3086, 0x1a03: 0x308a, 0x1a04: 0x308e, 0x1a05: 0x3092, - 0x1a06: 0x3096, 0x1a07: 0x2f7d, 0x1a08: 0x2e8d, 0x1a09: 0x309a, 0x1a0a: 0x2f81, 0x1a0b: 0x309e, - 0x1a0c: 0x2f85, 0x1a0d: 0x30a2, 0x1a0e: 0x1b76, 0x1a0f: 0x30a6, 0x1a10: 0x30ab, 0x1a11: 0x30b0, - 0x1a12: 0x30b5, 0x1a13: 0x30b9, 0x1a14: 0x30bd, 0x1a15: 0x30c1, 0x1a16: 0x30c6, 0x1a17: 0x30cb, - 0x1a18: 0x30d0, 0x1a19: 0x30d4, - // Block 0x69, offset 0x1a40 - 0x1a40: 0x30d8, 0x1a41: 0x30db, 0x1a42: 0x30de, 0x1a43: 0x30e1, 0x1a44: 0x30e5, 0x1a45: 0x30e9, - 0x1a46: 0x30e9, - 0x1a53: 0x30ec, 0x1a54: 0x30f1, 0x1a55: 0x30f6, 0x1a56: 0x30fb, 0x1a57: 0x3100, - 0x1a5d: 0x3105, - 0x1a5f: 0x310a, 0x1a60: 0x310f, 0x1a61: 0x14db, 0x1a62: 0x14e4, 0x1a63: 0x3112, - 0x1a64: 0x3115, 0x1a65: 0x3118, 0x1a66: 0x311b, 0x1a67: 0x311e, 0x1a68: 0x3121, 0x1a69: 0x1494, - 0x1a6a: 0x3124, 0x1a6b: 0x3129, 0x1a6c: 0x312e, 0x1a6d: 0x3135, 0x1a6e: 0x313c, 0x1a6f: 0x3141, - 0x1a70: 0x3146, 0x1a71: 0x314b, 0x1a72: 0x3150, 0x1a73: 0x3155, 0x1a74: 0x315a, 0x1a75: 0x315f, - 0x1a76: 0x3164, 0x1a78: 0x3169, 0x1a79: 0x316e, 0x1a7a: 0x3173, 0x1a7b: 0x3178, - 0x1a7c: 0x317d, 0x1a7e: 0x3182, - // Block 0x6a, offset 0x1a80 - 0x1a80: 0x3187, 0x1a81: 0x318c, 0x1a83: 0x3191, 0x1a84: 0x3196, - 0x1a86: 0x319b, 0x1a87: 0x31a0, 0x1a88: 0x31a5, 0x1a89: 0x31aa, 0x1a8a: 0x31af, 0x1a8b: 0x31b4, - 0x1a8c: 0x31b9, 0x1a8d: 0x31be, 0x1a8e: 0x31c3, 0x1a8f: 0x31c8, 0x1a90: 0x31cd, 0x1a91: 0x31cd, - 0x1a92: 0x31d0, 0x1a93: 0x31d0, 0x1a94: 0x31d0, 0x1a95: 0x31d0, 0x1a96: 0x31d3, 0x1a97: 0x31d3, - 0x1a98: 0x31d3, 0x1a99: 0x31d3, 0x1a9a: 0x31d6, 0x1a9b: 0x31d6, 0x1a9c: 0x31d6, 0x1a9d: 0x31d6, - 0x1a9e: 0x31d9, 0x1a9f: 0x31d9, 0x1aa0: 0x31d9, 0x1aa1: 0x31d9, 0x1aa2: 0x31dc, 0x1aa3: 0x31dc, - 0x1aa4: 0x31dc, 0x1aa5: 0x31dc, 0x1aa6: 0x31df, 0x1aa7: 0x31df, 0x1aa8: 0x31df, 0x1aa9: 0x31df, - 0x1aaa: 0x31e2, 0x1aab: 0x31e2, 0x1aac: 0x31e2, 0x1aad: 0x31e2, 0x1aae: 0x31e5, 0x1aaf: 0x31e5, - 0x1ab0: 0x31e5, 0x1ab1: 0x31e5, 0x1ab2: 0x31e8, 0x1ab3: 0x31e8, 0x1ab4: 0x31e8, 0x1ab5: 0x31e8, - 0x1ab6: 0x31eb, 0x1ab7: 0x31eb, 0x1ab8: 0x31eb, 0x1ab9: 0x31eb, 0x1aba: 0x31ee, 0x1abb: 0x31ee, - 0x1abc: 0x31ee, 0x1abd: 0x31ee, 0x1abe: 0x31f1, 0x1abf: 0x31f1, - // Block 0x6b, offset 0x1ac0 - 0x1ac0: 0x31f1, 0x1ac1: 0x31f1, 0x1ac2: 0x31f4, 0x1ac3: 0x31f4, 0x1ac4: 0x31f7, 0x1ac5: 0x31f7, - 0x1ac6: 0x31fa, 0x1ac7: 0x31fa, 0x1ac8: 0x31fd, 0x1ac9: 0x31fd, 0x1aca: 0x3200, 0x1acb: 0x3200, - 0x1acc: 0x3203, 0x1acd: 0x3203, 0x1ace: 0x3206, 0x1acf: 0x3206, 0x1ad0: 0x3206, 0x1ad1: 0x3206, - 0x1ad2: 0x3209, 0x1ad3: 0x3209, 0x1ad4: 0x3209, 0x1ad5: 0x3209, 0x1ad6: 0x320c, 0x1ad7: 0x320c, - 0x1ad8: 0x320c, 0x1ad9: 0x320c, 0x1ada: 0x320f, 0x1adb: 0x320f, 0x1adc: 0x320f, 0x1add: 0x320f, - 0x1ade: 0x3212, 0x1adf: 0x3212, 0x1ae0: 0x3215, 0x1ae1: 0x3215, 0x1ae2: 0x3215, 0x1ae3: 0x3215, - 0x1ae4: 0x06ba, 0x1ae5: 0x06ba, 0x1ae6: 0x3218, 0x1ae7: 0x3218, 0x1ae8: 0x3218, 0x1ae9: 0x3218, - 0x1aea: 0x321b, 0x1aeb: 0x321b, 0x1aec: 0x321b, 0x1aed: 0x321b, 0x1aee: 0x321e, 0x1aef: 0x321e, - 0x1af0: 0x06c4, 0x1af1: 0x06c4, - // Block 0x6c, offset 0x1b00 - 0x1b13: 0x3221, 0x1b14: 0x3221, 0x1b15: 0x3221, 0x1b16: 0x3221, 0x1b17: 0x3224, - 0x1b18: 0x3224, 0x1b19: 0x3227, 0x1b1a: 0x3227, 0x1b1b: 0x322a, 0x1b1c: 0x322a, 0x1b1d: 0x06b0, - 0x1b1e: 0x322d, 0x1b1f: 0x322d, 0x1b20: 0x3230, 0x1b21: 0x3230, 0x1b22: 0x3233, 0x1b23: 0x3233, - 0x1b24: 0x3236, 0x1b25: 0x3236, 0x1b26: 0x3236, 0x1b27: 0x3236, 0x1b28: 0x3239, 0x1b29: 0x3239, - 0x1b2a: 0x323c, 0x1b2b: 0x323c, 0x1b2c: 0x3243, 0x1b2d: 0x3243, 0x1b2e: 0x324a, 0x1b2f: 0x324a, - 0x1b30: 0x3251, 0x1b31: 0x3251, 0x1b32: 0x3258, 0x1b33: 0x3258, 0x1b34: 0x325f, 0x1b35: 0x325f, - 0x1b36: 0x3266, 0x1b37: 0x3266, 0x1b38: 0x3266, 0x1b39: 0x326d, 0x1b3a: 0x326d, 0x1b3b: 0x326d, - 0x1b3c: 0x3274, 0x1b3d: 0x3274, 0x1b3e: 0x3274, 0x1b3f: 0x3274, - // Block 0x6d, offset 0x1b40 - 0x1b40: 0x3277, 0x1b41: 0x327e, 0x1b42: 0x3285, 0x1b43: 0x326d, 0x1b44: 0x328c, 0x1b45: 0x3293, - 0x1b46: 0x3298, 0x1b47: 0x329d, 0x1b48: 0x32a2, 0x1b49: 0x32a7, 0x1b4a: 0x32ac, 0x1b4b: 0x32b1, - 0x1b4c: 0x32b6, 0x1b4d: 0x32bb, 0x1b4e: 0x32c0, 0x1b4f: 0x32c5, 0x1b50: 0x32ca, 0x1b51: 0x32cf, - 0x1b52: 0x32d4, 0x1b53: 0x32d9, 0x1b54: 0x32de, 0x1b55: 0x32e3, 0x1b56: 0x32e8, 0x1b57: 0x32ed, - 0x1b58: 0x32f2, 0x1b59: 0x32f7, 0x1b5a: 0x32fc, 0x1b5b: 0x3301, 0x1b5c: 0x3306, 0x1b5d: 0x330b, - 0x1b5e: 0x3310, 0x1b5f: 0x3315, 0x1b60: 0x331a, 0x1b61: 0x331f, 0x1b62: 0x3324, 0x1b63: 0x3329, - 0x1b64: 0x332e, 0x1b65: 0x3333, 0x1b66: 0x3338, 0x1b67: 0x333d, 0x1b68: 0x3342, 0x1b69: 0x3347, - 0x1b6a: 0x334c, 0x1b6b: 0x3351, 0x1b6c: 0x3356, 0x1b6d: 0x335b, 0x1b6e: 0x3360, 0x1b6f: 0x3365, - 0x1b70: 0x336a, 0x1b71: 0x336f, 0x1b72: 0x3374, 0x1b73: 0x3379, 0x1b74: 0x337e, 0x1b75: 0x3383, - 0x1b76: 0x3388, 0x1b77: 0x338d, 0x1b78: 0x3392, 0x1b79: 0x3397, 0x1b7a: 0x339c, 0x1b7b: 0x33a1, - 0x1b7c: 0x33a6, 0x1b7d: 0x33ab, 0x1b7e: 0x33b0, 0x1b7f: 0x33b5, - // Block 0x6e, offset 0x1b80 - 0x1b80: 0x33ba, 0x1b81: 0x33bf, 0x1b82: 0x33c4, 0x1b83: 0x33c9, 0x1b84: 0x33ce, 0x1b85: 0x33d3, - 0x1b86: 0x33d8, 0x1b87: 0x33dd, 0x1b88: 0x33e2, 0x1b89: 0x33e7, 0x1b8a: 0x33ec, 0x1b8b: 0x33f1, - 0x1b8c: 0x33f6, 0x1b8d: 0x33fb, 0x1b8e: 0x3400, 0x1b8f: 0x3405, 0x1b90: 0x340a, 0x1b91: 0x340f, - 0x1b92: 0x3414, 0x1b93: 0x3419, 0x1b94: 0x341e, 0x1b95: 0x3423, 0x1b96: 0x3428, 0x1b97: 0x342d, - 0x1b98: 0x3432, 0x1b99: 0x3437, 0x1b9a: 0x343c, 0x1b9b: 0x3441, 0x1b9c: 0x3446, 0x1b9d: 0x344b, - 0x1b9e: 0x3450, 0x1b9f: 0x3456, 0x1ba0: 0x345c, 0x1ba1: 0x3462, 0x1ba2: 0x3468, 0x1ba3: 0x346e, - 0x1ba4: 0x3474, 0x1ba5: 0x347b, 0x1ba6: 0x3285, 0x1ba7: 0x3482, 0x1ba8: 0x326d, 0x1ba9: 0x328c, - 0x1baa: 0x3489, 0x1bab: 0x348e, 0x1bac: 0x32a2, 0x1bad: 0x3493, 0x1bae: 0x32a7, 0x1baf: 0x32ac, - 0x1bb0: 0x3498, 0x1bb1: 0x349d, 0x1bb2: 0x32c0, 0x1bb3: 0x34a2, 0x1bb4: 0x32c5, 0x1bb5: 0x32ca, - 0x1bb6: 0x34a7, 0x1bb7: 0x34ac, 0x1bb8: 0x32d4, 0x1bb9: 0x34b1, 0x1bba: 0x32d9, 0x1bbb: 0x32de, - 0x1bbc: 0x336f, 0x1bbd: 0x3374, 0x1bbe: 0x3383, 0x1bbf: 0x3388, - // Block 0x6f, offset 0x1bc0 - 0x1bc0: 0x338d, 0x1bc1: 0x33a1, 0x1bc2: 0x33a6, 0x1bc3: 0x33ab, 0x1bc4: 0x33b0, 0x1bc5: 0x33c4, - 0x1bc6: 0x33c9, 0x1bc7: 0x33ce, 0x1bc8: 0x34b6, 0x1bc9: 0x33e2, 0x1bca: 0x34bb, 0x1bcb: 0x34c0, - 0x1bcc: 0x3400, 0x1bcd: 0x34c5, 0x1bce: 0x3405, 0x1bcf: 0x340a, 0x1bd0: 0x344b, 0x1bd1: 0x34ca, - 0x1bd2: 0x34cf, 0x1bd3: 0x3432, 0x1bd4: 0x34d4, 0x1bd5: 0x3437, 0x1bd6: 0x343c, 0x1bd7: 0x3277, - 0x1bd8: 0x327e, 0x1bd9: 0x34d9, 0x1bda: 0x3285, 0x1bdb: 0x34e0, 0x1bdc: 0x3293, 0x1bdd: 0x3298, - 0x1bde: 0x329d, 0x1bdf: 0x32a2, 0x1be0: 0x34e7, 0x1be1: 0x32b1, 0x1be2: 0x32b6, 0x1be3: 0x32bb, - 0x1be4: 0x32c0, 0x1be5: 0x34ec, 0x1be6: 0x32d4, 0x1be7: 0x32e3, 0x1be8: 0x32e8, 0x1be9: 0x32ed, - 0x1bea: 0x32f2, 0x1beb: 0x32f7, 0x1bec: 0x3301, 0x1bed: 0x3306, 0x1bee: 0x330b, 0x1bef: 0x3310, - 0x1bf0: 0x3315, 0x1bf1: 0x331a, 0x1bf2: 0x34f1, 0x1bf3: 0x331f, 0x1bf4: 0x3324, 0x1bf5: 0x3329, - 0x1bf6: 0x332e, 0x1bf7: 0x3333, 0x1bf8: 0x3338, 0x1bf9: 0x3342, 0x1bfa: 0x3347, 0x1bfb: 0x334c, - 0x1bfc: 0x3351, 0x1bfd: 0x3356, 0x1bfe: 0x335b, 0x1bff: 0x3360, - // Block 0x70, offset 0x1c00 - 0x1c00: 0x3365, 0x1c01: 0x336a, 0x1c02: 0x3379, 0x1c03: 0x337e, 0x1c04: 0x3392, 0x1c05: 0x3397, - 0x1c06: 0x339c, 0x1c07: 0x33a1, 0x1c08: 0x33a6, 0x1c09: 0x33b5, 0x1c0a: 0x33ba, 0x1c0b: 0x33bf, - 0x1c0c: 0x33c4, 0x1c0d: 0x34f6, 0x1c0e: 0x33d3, 0x1c0f: 0x33d8, 0x1c10: 0x33dd, 0x1c11: 0x33e2, - 0x1c12: 0x33f1, 0x1c13: 0x33f6, 0x1c14: 0x33fb, 0x1c15: 0x3400, 0x1c16: 0x34fb, 0x1c17: 0x340f, - 0x1c18: 0x3414, 0x1c19: 0x3500, 0x1c1a: 0x3423, 0x1c1b: 0x3428, 0x1c1c: 0x342d, 0x1c1d: 0x3432, - 0x1c1e: 0x3505, 0x1c1f: 0x3285, 0x1c20: 0x34e0, 0x1c21: 0x32a2, 0x1c22: 0x34e7, 0x1c23: 0x32c0, - 0x1c24: 0x34ec, 0x1c25: 0x32d4, 0x1c26: 0x350a, 0x1c27: 0x3315, 0x1c28: 0x350f, 0x1c29: 0x3514, - 0x1c2a: 0x3519, 0x1c2b: 0x33a1, 0x1c2c: 0x33a6, 0x1c2d: 0x33c4, 0x1c2e: 0x3400, 0x1c2f: 0x34fb, - 0x1c30: 0x3432, 0x1c31: 0x3505, 0x1c32: 0x351e, 0x1c33: 0x3525, 0x1c34: 0x352c, 0x1c35: 0x3533, - 0x1c36: 0x3538, 0x1c37: 0x353d, 0x1c38: 0x3542, 0x1c39: 0x3547, 0x1c3a: 0x354c, 0x1c3b: 0x3551, - 0x1c3c: 0x3556, 0x1c3d: 0x355b, 0x1c3e: 0x3560, 0x1c3f: 0x3565, - // Block 0x71, offset 0x1c40 - 0x1c40: 0x356a, 0x1c41: 0x356f, 0x1c42: 0x3574, 0x1c43: 0x3579, 0x1c44: 0x357e, 0x1c45: 0x3583, - 0x1c46: 0x3588, 0x1c47: 0x358d, 0x1c48: 0x3592, 0x1c49: 0x3597, 0x1c4a: 0x359c, 0x1c4b: 0x35a1, - 0x1c4c: 0x3514, 0x1c4d: 0x35a6, 0x1c4e: 0x35ab, 0x1c4f: 0x35b0, 0x1c50: 0x35b5, 0x1c51: 0x3533, - 0x1c52: 0x3538, 0x1c53: 0x353d, 0x1c54: 0x3542, 0x1c55: 0x3547, 0x1c56: 0x354c, 0x1c57: 0x3551, - 0x1c58: 0x3556, 0x1c59: 0x355b, 0x1c5a: 0x3560, 0x1c5b: 0x3565, 0x1c5c: 0x356a, 0x1c5d: 0x356f, - 0x1c5e: 0x3574, 0x1c5f: 0x3579, 0x1c60: 0x357e, 0x1c61: 0x3583, 0x1c62: 0x3588, 0x1c63: 0x358d, - 0x1c64: 0x3592, 0x1c65: 0x3597, 0x1c66: 0x359c, 0x1c67: 0x35a1, 0x1c68: 0x3514, 0x1c69: 0x35a6, - 0x1c6a: 0x35ab, 0x1c6b: 0x35b0, 0x1c6c: 0x35b5, 0x1c6d: 0x3597, 0x1c6e: 0x359c, 0x1c6f: 0x35a1, - 0x1c70: 0x3514, 0x1c71: 0x350f, 0x1c72: 0x3519, 0x1c73: 0x333d, 0x1c74: 0x3306, 0x1c75: 0x330b, - 0x1c76: 0x3310, 0x1c77: 0x3597, 0x1c78: 0x359c, 0x1c79: 0x35a1, 0x1c7a: 0x333d, 0x1c7b: 0x3342, - 0x1c7c: 0x35ba, 0x1c7d: 0x35ba, - // Block 0x72, offset 0x1c80 - 0x1c90: 0x35bf, 0x1c91: 0x35c6, - 0x1c92: 0x35c6, 0x1c93: 0x35cd, 0x1c94: 0x35d4, 0x1c95: 0x35db, 0x1c96: 0x35e2, 0x1c97: 0x35e9, - 0x1c98: 0x35f0, 0x1c99: 0x35f0, 0x1c9a: 0x35f7, 0x1c9b: 0x35fe, 0x1c9c: 0x3605, 0x1c9d: 0x360c, - 0x1c9e: 0x3613, 0x1c9f: 0x361a, 0x1ca0: 0x361a, 0x1ca1: 0x3621, 0x1ca2: 0x3628, 0x1ca3: 0x3628, - 0x1ca4: 0x362f, 0x1ca5: 0x362f, 0x1ca6: 0x3636, 0x1ca7: 0x363d, 0x1ca8: 0x363d, 0x1ca9: 0x3644, - 0x1caa: 0x364b, 0x1cab: 0x364b, 0x1cac: 0x3652, 0x1cad: 0x3652, 0x1cae: 0x3659, 0x1caf: 0x3660, - 0x1cb0: 0x3660, 0x1cb1: 0x3667, 0x1cb2: 0x3667, 0x1cb3: 0x366e, 0x1cb4: 0x3675, 0x1cb5: 0x367c, - 0x1cb6: 0x3683, 0x1cb7: 0x3683, 0x1cb8: 0x368a, 0x1cb9: 0x3691, 0x1cba: 0x3698, 0x1cbb: 0x369f, - 0x1cbc: 0x36a6, 0x1cbd: 0x36a6, 0x1cbe: 0x36ad, 0x1cbf: 0x36b4, - // Block 0x73, offset 0x1cc0 - 0x1cc0: 0x36bb, 0x1cc1: 0x36c2, 0x1cc2: 0x36c9, 0x1cc3: 0x36d0, 0x1cc4: 0x36d0, 0x1cc5: 0x36d7, - 0x1cc6: 0x36d7, 0x1cc7: 0x36de, 0x1cc8: 0x36de, 0x1cc9: 0x36e5, 0x1cca: 0x36ec, 0x1ccb: 0x36f3, - 0x1ccc: 0x36fa, 0x1ccd: 0x3701, 0x1cce: 0x3708, 0x1ccf: 0x370f, - 0x1cd2: 0x3716, 0x1cd3: 0x371d, 0x1cd4: 0x3724, 0x1cd5: 0x372b, 0x1cd6: 0x3732, 0x1cd7: 0x3739, - 0x1cd8: 0x3739, 0x1cd9: 0x3740, 0x1cda: 0x3747, 0x1cdb: 0x374e, 0x1cdc: 0x3755, 0x1cdd: 0x3755, - 0x1cde: 0x375c, 0x1cdf: 0x3763, 0x1ce0: 0x376a, 0x1ce1: 0x3771, 0x1ce2: 0x3778, 0x1ce3: 0x377f, - 0x1ce4: 0x3786, 0x1ce5: 0x378d, 0x1ce6: 0x3794, 0x1ce7: 0x379b, 0x1ce8: 0x37a2, 0x1ce9: 0x37a9, - 0x1cea: 0x37b0, 0x1ceb: 0x37b7, 0x1cec: 0x37be, 0x1ced: 0x37c5, 0x1cee: 0x37cc, 0x1cef: 0x37d3, - 0x1cf0: 0x37da, 0x1cf1: 0x37e1, 0x1cf2: 0x37e8, 0x1cf3: 0x37ef, 0x1cf4: 0x36ad, 0x1cf5: 0x36bb, - 0x1cf6: 0x37f6, 0x1cf7: 0x37fd, 0x1cf8: 0x3804, 0x1cf9: 0x380b, 0x1cfa: 0x3812, 0x1cfb: 0x3819, - 0x1cfc: 0x3812, 0x1cfd: 0x3804, 0x1cfe: 0x3820, 0x1cff: 0x3827, - // Block 0x74, offset 0x1d00 - 0x1d00: 0x382e, 0x1d01: 0x3835, 0x1d02: 0x383c, 0x1d03: 0x3819, 0x1d04: 0x367c, 0x1d05: 0x3636, - 0x1d06: 0x3843, 0x1d07: 0x384a, - 0x1d30: 0x3851, 0x1d31: 0x3858, 0x1d32: 0x385f, 0x1d33: 0x3868, 0x1d34: 0x3871, 0x1d35: 0x387a, - 0x1d36: 0x3883, 0x1d37: 0x388c, 0x1d38: 0x3895, 0x1d39: 0x389e, 0x1d3a: 0x38a5, 0x1d3b: 0x38c7, - 0x1d3c: 0x38d7, - // Block 0x75, offset 0x1d40 - 0x1d50: 0x38e0, 0x1d51: 0x38e2, - 0x1d52: 0x38e6, 0x1d53: 0x38ea, 0x1d54: 0x04e1, 0x1d55: 0x38ec, 0x1d56: 0x38ee, 0x1d57: 0x38f0, - 0x1d58: 0x38f4, 0x1d59: 0x1443, - 0x1d70: 0x1440, 0x1d71: 0x38f8, 0x1d72: 0x38fc, 0x1d73: 0x3900, 0x1d74: 0x3900, 0x1d75: 0x149c, - 0x1d76: 0x149e, 0x1d77: 0x3902, 0x1d78: 0x3904, 0x1d79: 0x3906, 0x1d7a: 0x390a, 0x1d7b: 0x390e, - 0x1d7c: 0x3912, 0x1d7d: 0x3916, 0x1d7e: 0x391a, 0x1d7f: 0x16c3, - // Block 0x76, offset 0x1d80 - 0x1d80: 0x16c7, 0x1d81: 0x391e, 0x1d82: 0x3922, 0x1d83: 0x3926, 0x1d84: 0x392a, - 0x1d87: 0x392e, 0x1d88: 0x3930, 0x1d89: 0x146c, 0x1d8a: 0x146c, 0x1d8b: 0x146c, - 0x1d8c: 0x146c, 0x1d8d: 0x3900, 0x1d8e: 0x3900, 0x1d8f: 0x3900, 0x1d90: 0x38e0, 0x1d91: 0x38e2, - 0x1d92: 0x143e, 0x1d94: 0x04e1, 0x1d95: 0x38ea, 0x1d96: 0x38ee, 0x1d97: 0x38ec, - 0x1d98: 0x38f8, 0x1d99: 0x149c, 0x1d9a: 0x149e, 0x1d9b: 0x3902, 0x1d9c: 0x3904, 0x1d9d: 0x3906, - 0x1d9e: 0x390a, 0x1d9f: 0x3932, 0x1da0: 0x3934, 0x1da1: 0x3936, 0x1da2: 0x1494, 0x1da3: 0x3938, - 0x1da4: 0x393a, 0x1da5: 0x393c, 0x1da6: 0x149a, 0x1da8: 0x393e, 0x1da9: 0x3940, - 0x1daa: 0x3942, 0x1dab: 0x3944, - 0x1db0: 0x3946, 0x1db1: 0x394a, 0x1db2: 0x394f, 0x1db4: 0x3953, - 0x1db6: 0x3957, 0x1db7: 0x395b, 0x1db8: 0x3960, 0x1db9: 0x3964, 0x1dba: 0x3969, 0x1dbb: 0x396d, - 0x1dbc: 0x3972, 0x1dbd: 0x3976, 0x1dbe: 0x397b, 0x1dbf: 0x397f, - // Block 0x77, offset 0x1dc0 - 0x1dc0: 0x3984, 0x1dc1: 0x068d, 0x1dc2: 0x068d, 0x1dc3: 0x0692, 0x1dc4: 0x0692, 0x1dc5: 0x0697, - 0x1dc6: 0x0697, 0x1dc7: 0x069c, 0x1dc8: 0x069c, 0x1dc9: 0x06a1, 0x1dca: 0x06a1, 0x1dcb: 0x06a1, - 0x1dcc: 0x06a1, 0x1dcd: 0x3987, 0x1dce: 0x3987, 0x1dcf: 0x398a, 0x1dd0: 0x398a, 0x1dd1: 0x398a, - 0x1dd2: 0x398a, 0x1dd3: 0x398d, 0x1dd4: 0x398d, 0x1dd5: 0x3990, 0x1dd6: 0x3990, 0x1dd7: 0x3990, - 0x1dd8: 0x3990, 0x1dd9: 0x3993, 0x1dda: 0x3993, 0x1ddb: 0x3993, 0x1ddc: 0x3993, 0x1ddd: 0x3996, - 0x1dde: 0x3996, 0x1ddf: 0x3996, 0x1de0: 0x3996, 0x1de1: 0x3999, 0x1de2: 0x3999, 0x1de3: 0x3999, - 0x1de4: 0x3999, 0x1de5: 0x399c, 0x1de6: 0x399c, 0x1de7: 0x399c, 0x1de8: 0x399c, 0x1de9: 0x399f, - 0x1dea: 0x399f, 0x1deb: 0x39a2, 0x1dec: 0x39a2, 0x1ded: 0x39a5, 0x1dee: 0x39a5, 0x1def: 0x39a8, - 0x1df0: 0x39a8, 0x1df1: 0x39ab, 0x1df2: 0x39ab, 0x1df3: 0x39ab, 0x1df4: 0x39ab, 0x1df5: 0x39ae, - 0x1df6: 0x39ae, 0x1df7: 0x39ae, 0x1df8: 0x39ae, 0x1df9: 0x39b1, 0x1dfa: 0x39b1, 0x1dfb: 0x39b1, - 0x1dfc: 0x39b1, 0x1dfd: 0x39b4, 0x1dfe: 0x39b4, 0x1dff: 0x39b4, - // Block 0x78, offset 0x1e00 - 0x1e00: 0x39b4, 0x1e01: 0x39b7, 0x1e02: 0x39b7, 0x1e03: 0x39b7, 0x1e04: 0x39b7, 0x1e05: 0x39ba, - 0x1e06: 0x39ba, 0x1e07: 0x39ba, 0x1e08: 0x39ba, 0x1e09: 0x39bd, 0x1e0a: 0x39bd, 0x1e0b: 0x39bd, - 0x1e0c: 0x39bd, 0x1e0d: 0x39c0, 0x1e0e: 0x39c0, 0x1e0f: 0x39c0, 0x1e10: 0x39c0, 0x1e11: 0x39c3, - 0x1e12: 0x39c3, 0x1e13: 0x39c3, 0x1e14: 0x39c3, 0x1e15: 0x39c6, 0x1e16: 0x39c6, 0x1e17: 0x39c6, - 0x1e18: 0x39c6, 0x1e19: 0x39c9, 0x1e1a: 0x39c9, 0x1e1b: 0x39c9, 0x1e1c: 0x39c9, 0x1e1d: 0x39cc, - 0x1e1e: 0x39cc, 0x1e1f: 0x39cc, 0x1e20: 0x39cc, 0x1e21: 0x39cf, 0x1e22: 0x39cf, 0x1e23: 0x39cf, - 0x1e24: 0x39cf, 0x1e25: 0x39d2, 0x1e26: 0x39d2, 0x1e27: 0x39d2, 0x1e28: 0x39d2, 0x1e29: 0x39d5, - 0x1e2a: 0x39d5, 0x1e2b: 0x39d5, 0x1e2c: 0x39d5, 0x1e2d: 0x39d8, 0x1e2e: 0x39d8, 0x1e2f: 0x3239, - 0x1e30: 0x3239, 0x1e31: 0x39db, 0x1e32: 0x39db, 0x1e33: 0x39db, 0x1e34: 0x39db, 0x1e35: 0x39de, - 0x1e36: 0x39de, 0x1e37: 0x39e5, 0x1e38: 0x39e5, 0x1e39: 0x39ec, 0x1e3a: 0x39ec, 0x1e3b: 0x39f3, - 0x1e3c: 0x39f3, - // Block 0x79, offset 0x1e40 - 0x1e41: 0x38ec, 0x1e42: 0x39f8, 0x1e43: 0x3932, 0x1e44: 0x3940, 0x1e45: 0x3942, - 0x1e46: 0x3934, 0x1e47: 0x39fa, 0x1e48: 0x149c, 0x1e49: 0x149e, 0x1e4a: 0x3936, 0x1e4b: 0x1494, - 0x1e4c: 0x38e0, 0x1e4d: 0x3938, 0x1e4e: 0x143e, 0x1e4f: 0x39fc, 0x1e50: 0x1486, 0x1e51: 0x001c, - 0x1e52: 0x000d, 0x1e53: 0x000f, 0x1e54: 0x1488, 0x1e55: 0x148a, 0x1e56: 0x148c, 0x1e57: 0x148e, - 0x1e58: 0x1490, 0x1e59: 0x1492, 0x1e5a: 0x38ea, 0x1e5b: 0x04e1, 0x1e5c: 0x393a, 0x1e5d: 0x149a, - 0x1e5e: 0x393c, 0x1e5f: 0x38ee, 0x1e60: 0x3944, 0x1e61: 0x0906, 0x1e62: 0x090b, 0x1e63: 0x14ad, - 0x1e64: 0x090d, 0x1e65: 0x090f, 0x1e66: 0x14d9, 0x1e67: 0x0914, 0x1e68: 0x0916, 0x1e69: 0x0918, - 0x1e6a: 0x091a, 0x1e6b: 0x091c, 0x1e6c: 0x091e, 0x1e6d: 0x0920, 0x1e6e: 0x0922, 0x1e6f: 0x0924, - 0x1e70: 0x0929, 0x1e71: 0x14c8, 0x1e72: 0x092b, 0x1e73: 0x17f6, 0x1e74: 0x092d, 0x1e75: 0x092f, - 0x1e76: 0x155f, 0x1e77: 0x0931, 0x1e78: 0x1570, 0x1e79: 0x17f8, 0x1e7a: 0x14d4, 0x1e7b: 0x392e, - 0x1e7c: 0x393e, 0x1e7d: 0x3930, 0x1e7e: 0x39fe, 0x1e7f: 0x3900, - // Block 0x7a, offset 0x1e80 - 0x1e80: 0x13f7, 0x1e81: 0x0007, 0x1e82: 0x093d, 0x1e83: 0x0984, 0x1e84: 0x093f, 0x1e85: 0x0941, - 0x1e86: 0x098c, 0x1e87: 0x094c, 0x1e88: 0x0494, 0x1e89: 0x097c, 0x1e8a: 0x0499, 0x1e8b: 0x094e, - 0x1e8c: 0x04c5, 0x1e8d: 0x0950, 0x1e8e: 0x14a0, 0x1e8f: 0x001e, 0x1e90: 0x0960, 0x1e91: 0x17fa, - 0x1e92: 0x049b, 0x1e93: 0x02c8, 0x1e94: 0x0962, 0x1e95: 0x0964, 0x1e96: 0x096d, 0x1e97: 0x04a6, - 0x1e98: 0x04c7, 0x1e99: 0x04a8, 0x1e9a: 0x09df, 0x1e9b: 0x3902, 0x1e9c: 0x3a00, 0x1e9d: 0x3904, - 0x1e9e: 0x3a02, 0x1e9f: 0x3a04, 0x1ea0: 0x3a08, 0x1ea1: 0x38e6, 0x1ea2: 0x391e, 0x1ea3: 0x3922, - 0x1ea4: 0x38e2, 0x1ea5: 0x3a0c, 0x1ea6: 0x2321, 0x1ea7: 0x3a10, 0x1ea8: 0x3a14, 0x1ea9: 0x3a18, - 0x1eaa: 0x3a1c, 0x1eab: 0x3a20, 0x1eac: 0x3a24, 0x1ead: 0x3a28, 0x1eae: 0x3a2c, 0x1eaf: 0x3a30, - 0x1eb0: 0x3a34, 0x1eb1: 0x2269, 0x1eb2: 0x226d, 0x1eb3: 0x2271, 0x1eb4: 0x2275, 0x1eb5: 0x2279, - 0x1eb6: 0x227d, 0x1eb7: 0x2281, 0x1eb8: 0x2285, 0x1eb9: 0x2289, 0x1eba: 0x228d, 0x1ebb: 0x2291, - 0x1ebc: 0x2295, 0x1ebd: 0x2299, 0x1ebe: 0x229d, 0x1ebf: 0x22a1, - // Block 0x7b, offset 0x1ec0 - 0x1ec0: 0x22a5, 0x1ec1: 0x22a9, 0x1ec2: 0x22ad, 0x1ec3: 0x22b1, 0x1ec4: 0x22b5, 0x1ec5: 0x22b9, - 0x1ec6: 0x22bd, 0x1ec7: 0x22c1, 0x1ec8: 0x22c5, 0x1ec9: 0x22c9, 0x1eca: 0x22cd, 0x1ecb: 0x22d1, - 0x1ecc: 0x22d5, 0x1ecd: 0x22d9, 0x1ece: 0x22dd, 0x1ecf: 0x22e1, 0x1ed0: 0x22e5, 0x1ed1: 0x22e9, - 0x1ed2: 0x22ed, 0x1ed3: 0x22f1, 0x1ed4: 0x22f5, 0x1ed5: 0x22f9, 0x1ed6: 0x22fd, 0x1ed7: 0x2301, - 0x1ed8: 0x2305, 0x1ed9: 0x2309, 0x1eda: 0x230d, 0x1edb: 0x2311, 0x1edc: 0x2315, 0x1edd: 0x3a38, - 0x1ede: 0x3a3c, 0x1edf: 0x3a40, 0x1ee0: 0x1e04, 0x1ee1: 0x1d38, 0x1ee2: 0x1d3c, 0x1ee3: 0x1d40, - 0x1ee4: 0x1d44, 0x1ee5: 0x1d48, 0x1ee6: 0x1d4c, 0x1ee7: 0x1d50, 0x1ee8: 0x1d54, 0x1ee9: 0x1d58, - 0x1eea: 0x1d5c, 0x1eeb: 0x1d60, 0x1eec: 0x1d64, 0x1eed: 0x1d68, 0x1eee: 0x1d6c, 0x1eef: 0x1d70, - 0x1ef0: 0x1d74, 0x1ef1: 0x1d78, 0x1ef2: 0x1d7c, 0x1ef3: 0x1d80, 0x1ef4: 0x1d84, 0x1ef5: 0x1d88, - 0x1ef6: 0x1d8c, 0x1ef7: 0x1d90, 0x1ef8: 0x1d94, 0x1ef9: 0x1d98, 0x1efa: 0x1d9c, 0x1efb: 0x1da0, - 0x1efc: 0x1da4, 0x1efd: 0x1da8, 0x1efe: 0x1dac, - // Block 0x7c, offset 0x1f00 - 0x1f02: 0x1db0, 0x1f03: 0x1db4, 0x1f04: 0x1db8, 0x1f05: 0x1dbc, - 0x1f06: 0x1dc0, 0x1f07: 0x1dc4, 0x1f0a: 0x1dc8, 0x1f0b: 0x1dcc, - 0x1f0c: 0x1dd0, 0x1f0d: 0x1dd4, 0x1f0e: 0x1dd8, 0x1f0f: 0x1ddc, - 0x1f12: 0x1de0, 0x1f13: 0x1de4, 0x1f14: 0x1de8, 0x1f15: 0x1dec, 0x1f16: 0x1df0, 0x1f17: 0x1df4, - 0x1f1a: 0x1df8, 0x1f1b: 0x1dfc, 0x1f1c: 0x1e00, - 0x1f20: 0x3a44, 0x1f21: 0x3a47, 0x1f22: 0x3a4a, 0x1f23: 0x0009, - 0x1f24: 0x3a4d, 0x1f25: 0x3a50, 0x1f26: 0x3a53, 0x1f28: 0x3a57, 0x1f29: 0x3a5b, - 0x1f2a: 0x3a5f, 0x1f2b: 0x3a63, 0x1f2c: 0x3a67, 0x1f2d: 0x3a6b, 0x1f2e: 0x3a6f, - // Block 0x7d, offset 0x1f40 - 0x1f5a: 0x3a73, 0x1f5c: 0x3a7c, - 0x1f6b: 0x3a85, - // Block 0x7e, offset 0x1f80 - 0x1f9e: 0x3a8e, 0x1f9f: 0x3a97, 0x1fa0: 0x3aa0, 0x1fa1: 0x3aad, 0x1fa2: 0x3aba, 0x1fa3: 0x3ac7, - 0x1fa4: 0x3ad4, - // Block 0x7f, offset 0x1fc0 - 0x1ffb: 0x3ae1, - 0x1ffc: 0x3aea, 0x1ffd: 0x3af3, 0x1ffe: 0x3b00, 0x1fff: 0x3b0d, - // Block 0x80, offset 0x2000 - 0x2000: 0x3b1a, - // Block 0x81, offset 0x2040 - 0x2040: 0x0906, 0x2041: 0x090b, 0x2042: 0x14ad, 0x2043: 0x090d, 0x2044: 0x090f, 0x2045: 0x14d9, - 0x2046: 0x0914, 0x2047: 0x0916, 0x2048: 0x0918, 0x2049: 0x091a, 0x204a: 0x091c, 0x204b: 0x091e, - 0x204c: 0x0920, 0x204d: 0x0922, 0x204e: 0x0924, 0x204f: 0x0929, 0x2050: 0x14c8, 0x2051: 0x092b, - 0x2052: 0x17f6, 0x2053: 0x092d, 0x2054: 0x092f, 0x2055: 0x155f, 0x2056: 0x0931, 0x2057: 0x1570, - 0x2058: 0x17f8, 0x2059: 0x14d4, 0x205a: 0x0007, 0x205b: 0x093d, 0x205c: 0x0984, 0x205d: 0x093f, - 0x205e: 0x0941, 0x205f: 0x098c, 0x2060: 0x094c, 0x2061: 0x0494, 0x2062: 0x097c, 0x2063: 0x0499, - 0x2064: 0x094e, 0x2065: 0x04c5, 0x2066: 0x0950, 0x2067: 0x14a0, 0x2068: 0x001e, 0x2069: 0x0960, - 0x206a: 0x17fa, 0x206b: 0x049b, 0x206c: 0x02c8, 0x206d: 0x0962, 0x206e: 0x0964, 0x206f: 0x096d, - 0x2070: 0x04a6, 0x2071: 0x04c7, 0x2072: 0x04a8, 0x2073: 0x09df, 0x2074: 0x0906, 0x2075: 0x090b, - 0x2076: 0x14ad, 0x2077: 0x090d, 0x2078: 0x090f, 0x2079: 0x14d9, 0x207a: 0x0914, 0x207b: 0x0916, - 0x207c: 0x0918, 0x207d: 0x091a, 0x207e: 0x091c, 0x207f: 0x091e, - // Block 0x82, offset 0x2080 - 0x2080: 0x0920, 0x2081: 0x0922, 0x2082: 0x0924, 0x2083: 0x0929, 0x2084: 0x14c8, 0x2085: 0x092b, - 0x2086: 0x17f6, 0x2087: 0x092d, 0x2088: 0x092f, 0x2089: 0x155f, 0x208a: 0x0931, 0x208b: 0x1570, - 0x208c: 0x17f8, 0x208d: 0x14d4, 0x208e: 0x0007, 0x208f: 0x093d, 0x2090: 0x0984, 0x2091: 0x093f, - 0x2092: 0x0941, 0x2093: 0x098c, 0x2094: 0x094c, 0x2096: 0x097c, 0x2097: 0x0499, - 0x2098: 0x094e, 0x2099: 0x04c5, 0x209a: 0x0950, 0x209b: 0x14a0, 0x209c: 0x001e, 0x209d: 0x0960, - 0x209e: 0x17fa, 0x209f: 0x049b, 0x20a0: 0x02c8, 0x20a1: 0x0962, 0x20a2: 0x0964, 0x20a3: 0x096d, - 0x20a4: 0x04a6, 0x20a5: 0x04c7, 0x20a6: 0x04a8, 0x20a7: 0x09df, 0x20a8: 0x0906, 0x20a9: 0x090b, - 0x20aa: 0x14ad, 0x20ab: 0x090d, 0x20ac: 0x090f, 0x20ad: 0x14d9, 0x20ae: 0x0914, 0x20af: 0x0916, - 0x20b0: 0x0918, 0x20b1: 0x091a, 0x20b2: 0x091c, 0x20b3: 0x091e, 0x20b4: 0x0920, 0x20b5: 0x0922, - 0x20b6: 0x0924, 0x20b7: 0x0929, 0x20b8: 0x14c8, 0x20b9: 0x092b, 0x20ba: 0x17f6, 0x20bb: 0x092d, - 0x20bc: 0x092f, 0x20bd: 0x155f, 0x20be: 0x0931, 0x20bf: 0x1570, - // Block 0x83, offset 0x20c0 - 0x20c0: 0x17f8, 0x20c1: 0x14d4, 0x20c2: 0x0007, 0x20c3: 0x093d, 0x20c4: 0x0984, 0x20c5: 0x093f, - 0x20c6: 0x0941, 0x20c7: 0x098c, 0x20c8: 0x094c, 0x20c9: 0x0494, 0x20ca: 0x097c, 0x20cb: 0x0499, - 0x20cc: 0x094e, 0x20cd: 0x04c5, 0x20ce: 0x0950, 0x20cf: 0x14a0, 0x20d0: 0x001e, 0x20d1: 0x0960, - 0x20d2: 0x17fa, 0x20d3: 0x049b, 0x20d4: 0x02c8, 0x20d5: 0x0962, 0x20d6: 0x0964, 0x20d7: 0x096d, - 0x20d8: 0x04a6, 0x20d9: 0x04c7, 0x20da: 0x04a8, 0x20db: 0x09df, 0x20dc: 0x0906, - 0x20de: 0x14ad, 0x20df: 0x090d, 0x20e2: 0x0914, - 0x20e5: 0x091a, 0x20e6: 0x091c, 0x20e9: 0x0922, - 0x20ea: 0x0924, 0x20eb: 0x0929, 0x20ec: 0x14c8, 0x20ee: 0x17f6, 0x20ef: 0x092d, - 0x20f0: 0x092f, 0x20f1: 0x155f, 0x20f2: 0x0931, 0x20f3: 0x1570, 0x20f4: 0x17f8, 0x20f5: 0x14d4, - 0x20f6: 0x0007, 0x20f7: 0x093d, 0x20f8: 0x0984, 0x20f9: 0x093f, 0x20fb: 0x098c, - 0x20fd: 0x0494, 0x20fe: 0x097c, 0x20ff: 0x0499, - // Block 0x84, offset 0x2100 - 0x2100: 0x094e, 0x2101: 0x04c5, 0x2102: 0x0950, 0x2103: 0x14a0, 0x2105: 0x0960, - 0x2106: 0x17fa, 0x2107: 0x049b, 0x2108: 0x02c8, 0x2109: 0x0962, 0x210a: 0x0964, 0x210b: 0x096d, - 0x210c: 0x04a6, 0x210d: 0x04c7, 0x210e: 0x04a8, 0x210f: 0x09df, 0x2110: 0x0906, 0x2111: 0x090b, - 0x2112: 0x14ad, 0x2113: 0x090d, 0x2114: 0x090f, 0x2115: 0x14d9, 0x2116: 0x0914, 0x2117: 0x0916, - 0x2118: 0x0918, 0x2119: 0x091a, 0x211a: 0x091c, 0x211b: 0x091e, 0x211c: 0x0920, 0x211d: 0x0922, - 0x211e: 0x0924, 0x211f: 0x0929, 0x2120: 0x14c8, 0x2121: 0x092b, 0x2122: 0x17f6, 0x2123: 0x092d, - 0x2124: 0x092f, 0x2125: 0x155f, 0x2126: 0x0931, 0x2127: 0x1570, 0x2128: 0x17f8, 0x2129: 0x14d4, - 0x212a: 0x0007, 0x212b: 0x093d, 0x212c: 0x0984, 0x212d: 0x093f, 0x212e: 0x0941, 0x212f: 0x098c, - 0x2130: 0x094c, 0x2131: 0x0494, 0x2132: 0x097c, 0x2133: 0x0499, 0x2134: 0x094e, 0x2135: 0x04c5, - 0x2136: 0x0950, 0x2137: 0x14a0, 0x2138: 0x001e, 0x2139: 0x0960, 0x213a: 0x17fa, 0x213b: 0x049b, - 0x213c: 0x02c8, 0x213d: 0x0962, 0x213e: 0x0964, 0x213f: 0x096d, - // Block 0x85, offset 0x2140 - 0x2140: 0x04a6, 0x2141: 0x04c7, 0x2142: 0x04a8, 0x2143: 0x09df, 0x2144: 0x0906, 0x2145: 0x090b, - 0x2147: 0x090d, 0x2148: 0x090f, 0x2149: 0x14d9, 0x214a: 0x0914, - 0x214d: 0x091a, 0x214e: 0x091c, 0x214f: 0x091e, 0x2150: 0x0920, 0x2151: 0x0922, - 0x2152: 0x0924, 0x2153: 0x0929, 0x2154: 0x14c8, 0x2156: 0x17f6, 0x2157: 0x092d, - 0x2158: 0x092f, 0x2159: 0x155f, 0x215a: 0x0931, 0x215b: 0x1570, 0x215c: 0x17f8, - 0x215e: 0x0007, 0x215f: 0x093d, 0x2160: 0x0984, 0x2161: 0x093f, 0x2162: 0x0941, 0x2163: 0x098c, - 0x2164: 0x094c, 0x2165: 0x0494, 0x2166: 0x097c, 0x2167: 0x0499, 0x2168: 0x094e, 0x2169: 0x04c5, - 0x216a: 0x0950, 0x216b: 0x14a0, 0x216c: 0x001e, 0x216d: 0x0960, 0x216e: 0x17fa, 0x216f: 0x049b, - 0x2170: 0x02c8, 0x2171: 0x0962, 0x2172: 0x0964, 0x2173: 0x096d, 0x2174: 0x04a6, 0x2175: 0x04c7, - 0x2176: 0x04a8, 0x2177: 0x09df, 0x2178: 0x0906, 0x2179: 0x090b, 0x217b: 0x090d, - 0x217c: 0x090f, 0x217d: 0x14d9, 0x217e: 0x0914, - // Block 0x86, offset 0x2180 - 0x2180: 0x0918, 0x2181: 0x091a, 0x2182: 0x091c, 0x2183: 0x091e, 0x2184: 0x0920, - 0x2186: 0x0924, 0x218a: 0x17f6, 0x218b: 0x092d, - 0x218c: 0x092f, 0x218d: 0x155f, 0x218e: 0x0931, 0x218f: 0x1570, 0x2190: 0x17f8, - 0x2192: 0x0007, 0x2193: 0x093d, 0x2194: 0x0984, 0x2195: 0x093f, 0x2196: 0x0941, 0x2197: 0x098c, - 0x2198: 0x094c, 0x2199: 0x0494, 0x219a: 0x097c, 0x219b: 0x0499, 0x219c: 0x094e, 0x219d: 0x04c5, - 0x219e: 0x0950, 0x219f: 0x14a0, 0x21a0: 0x001e, 0x21a1: 0x0960, 0x21a2: 0x17fa, 0x21a3: 0x049b, - 0x21a4: 0x02c8, 0x21a5: 0x0962, 0x21a6: 0x0964, 0x21a7: 0x096d, 0x21a8: 0x04a6, 0x21a9: 0x04c7, - 0x21aa: 0x04a8, 0x21ab: 0x09df, 0x21ac: 0x0906, 0x21ad: 0x090b, 0x21ae: 0x14ad, 0x21af: 0x090d, - 0x21b0: 0x090f, 0x21b1: 0x14d9, 0x21b2: 0x0914, 0x21b3: 0x0916, 0x21b4: 0x0918, 0x21b5: 0x091a, - 0x21b6: 0x091c, 0x21b7: 0x091e, 0x21b8: 0x0920, 0x21b9: 0x0922, 0x21ba: 0x0924, 0x21bb: 0x0929, - 0x21bc: 0x14c8, 0x21bd: 0x092b, 0x21be: 0x17f6, 0x21bf: 0x092d, - // Block 0x87, offset 0x21c0 - 0x21c0: 0x092f, 0x21c1: 0x155f, 0x21c2: 0x0931, 0x21c3: 0x1570, 0x21c4: 0x17f8, 0x21c5: 0x14d4, - 0x21c6: 0x0007, 0x21c7: 0x093d, 0x21c8: 0x0984, 0x21c9: 0x093f, 0x21ca: 0x0941, 0x21cb: 0x098c, - 0x21cc: 0x094c, 0x21cd: 0x0494, 0x21ce: 0x097c, 0x21cf: 0x0499, 0x21d0: 0x094e, 0x21d1: 0x04c5, - 0x21d2: 0x0950, 0x21d3: 0x14a0, 0x21d4: 0x001e, 0x21d5: 0x0960, 0x21d6: 0x17fa, 0x21d7: 0x049b, - 0x21d8: 0x02c8, 0x21d9: 0x0962, 0x21da: 0x0964, 0x21db: 0x096d, 0x21dc: 0x04a6, 0x21dd: 0x04c7, - 0x21de: 0x04a8, 0x21df: 0x09df, 0x21e0: 0x0906, 0x21e1: 0x090b, 0x21e2: 0x14ad, 0x21e3: 0x090d, - 0x21e4: 0x090f, 0x21e5: 0x14d9, 0x21e6: 0x0914, 0x21e7: 0x0916, 0x21e8: 0x0918, 0x21e9: 0x091a, - 0x21ea: 0x091c, 0x21eb: 0x091e, 0x21ec: 0x0920, 0x21ed: 0x0922, 0x21ee: 0x0924, 0x21ef: 0x0929, - 0x21f0: 0x14c8, 0x21f1: 0x092b, 0x21f2: 0x17f6, 0x21f3: 0x092d, 0x21f4: 0x092f, 0x21f5: 0x155f, - 0x21f6: 0x0931, 0x21f7: 0x1570, 0x21f8: 0x17f8, 0x21f9: 0x14d4, 0x21fa: 0x0007, 0x21fb: 0x093d, - 0x21fc: 0x0984, 0x21fd: 0x093f, 0x21fe: 0x0941, 0x21ff: 0x098c, - // Block 0x88, offset 0x2200 - 0x2200: 0x094c, 0x2201: 0x0494, 0x2202: 0x097c, 0x2203: 0x0499, 0x2204: 0x094e, 0x2205: 0x04c5, - 0x2206: 0x0950, 0x2207: 0x14a0, 0x2208: 0x001e, 0x2209: 0x0960, 0x220a: 0x17fa, 0x220b: 0x049b, - 0x220c: 0x02c8, 0x220d: 0x0962, 0x220e: 0x0964, 0x220f: 0x096d, 0x2210: 0x04a6, 0x2211: 0x04c7, - 0x2212: 0x04a8, 0x2213: 0x09df, 0x2214: 0x0906, 0x2215: 0x090b, 0x2216: 0x14ad, 0x2217: 0x090d, - 0x2218: 0x090f, 0x2219: 0x14d9, 0x221a: 0x0914, 0x221b: 0x0916, 0x221c: 0x0918, 0x221d: 0x091a, - 0x221e: 0x091c, 0x221f: 0x091e, 0x2220: 0x0920, 0x2221: 0x0922, 0x2222: 0x0924, 0x2223: 0x0929, - 0x2224: 0x14c8, 0x2225: 0x092b, 0x2226: 0x17f6, 0x2227: 0x092d, 0x2228: 0x092f, 0x2229: 0x155f, - 0x222a: 0x0931, 0x222b: 0x1570, 0x222c: 0x17f8, 0x222d: 0x14d4, 0x222e: 0x0007, 0x222f: 0x093d, - 0x2230: 0x0984, 0x2231: 0x093f, 0x2232: 0x0941, 0x2233: 0x098c, 0x2234: 0x094c, 0x2235: 0x0494, - 0x2236: 0x097c, 0x2237: 0x0499, 0x2238: 0x094e, 0x2239: 0x04c5, 0x223a: 0x0950, 0x223b: 0x14a0, - 0x223c: 0x001e, 0x223d: 0x0960, 0x223e: 0x17fa, 0x223f: 0x049b, - // Block 0x89, offset 0x2240 - 0x2240: 0x02c8, 0x2241: 0x0962, 0x2242: 0x0964, 0x2243: 0x096d, 0x2244: 0x04a6, 0x2245: 0x04c7, - 0x2246: 0x04a8, 0x2247: 0x09df, 0x2248: 0x0906, 0x2249: 0x090b, 0x224a: 0x14ad, 0x224b: 0x090d, - 0x224c: 0x090f, 0x224d: 0x14d9, 0x224e: 0x0914, 0x224f: 0x0916, 0x2250: 0x0918, 0x2251: 0x091a, - 0x2252: 0x091c, 0x2253: 0x091e, 0x2254: 0x0920, 0x2255: 0x0922, 0x2256: 0x0924, 0x2257: 0x0929, - 0x2258: 0x14c8, 0x2259: 0x092b, 0x225a: 0x17f6, 0x225b: 0x092d, 0x225c: 0x092f, 0x225d: 0x155f, - 0x225e: 0x0931, 0x225f: 0x1570, 0x2260: 0x17f8, 0x2261: 0x14d4, 0x2262: 0x0007, 0x2263: 0x093d, - 0x2264: 0x0984, 0x2265: 0x093f, 0x2266: 0x0941, 0x2267: 0x098c, 0x2268: 0x094c, 0x2269: 0x0494, - 0x226a: 0x097c, 0x226b: 0x0499, 0x226c: 0x094e, 0x226d: 0x04c5, 0x226e: 0x0950, 0x226f: 0x14a0, - 0x2270: 0x001e, 0x2271: 0x0960, 0x2272: 0x17fa, 0x2273: 0x049b, 0x2274: 0x02c8, 0x2275: 0x0962, - 0x2276: 0x0964, 0x2277: 0x096d, 0x2278: 0x04a6, 0x2279: 0x04c7, 0x227a: 0x04a8, 0x227b: 0x09df, - 0x227c: 0x0906, 0x227d: 0x090b, 0x227e: 0x14ad, 0x227f: 0x090d, - // Block 0x8a, offset 0x2280 - 0x2280: 0x090f, 0x2281: 0x14d9, 0x2282: 0x0914, 0x2283: 0x0916, 0x2284: 0x0918, 0x2285: 0x091a, - 0x2286: 0x091c, 0x2287: 0x091e, 0x2288: 0x0920, 0x2289: 0x0922, 0x228a: 0x0924, 0x228b: 0x0929, - 0x228c: 0x14c8, 0x228d: 0x092b, 0x228e: 0x17f6, 0x228f: 0x092d, 0x2290: 0x092f, 0x2291: 0x155f, - 0x2292: 0x0931, 0x2293: 0x1570, 0x2294: 0x17f8, 0x2295: 0x14d4, 0x2296: 0x0007, 0x2297: 0x093d, - 0x2298: 0x0984, 0x2299: 0x093f, 0x229a: 0x0941, 0x229b: 0x098c, 0x229c: 0x094c, 0x229d: 0x0494, - 0x229e: 0x097c, 0x229f: 0x0499, 0x22a0: 0x094e, 0x22a1: 0x04c5, 0x22a2: 0x0950, 0x22a3: 0x14a0, - 0x22a4: 0x001e, 0x22a5: 0x0960, 0x22a6: 0x17fa, 0x22a7: 0x049b, 0x22a8: 0x02c8, 0x22a9: 0x0962, - 0x22aa: 0x0964, 0x22ab: 0x096d, 0x22ac: 0x04a6, 0x22ad: 0x04c7, 0x22ae: 0x04a8, 0x22af: 0x09df, - 0x22b0: 0x0906, 0x22b1: 0x090b, 0x22b2: 0x14ad, 0x22b3: 0x090d, 0x22b4: 0x090f, 0x22b5: 0x14d9, - 0x22b6: 0x0914, 0x22b7: 0x0916, 0x22b8: 0x0918, 0x22b9: 0x091a, 0x22ba: 0x091c, 0x22bb: 0x091e, - 0x22bc: 0x0920, 0x22bd: 0x0922, 0x22be: 0x0924, 0x22bf: 0x0929, - // Block 0x8b, offset 0x22c0 - 0x22c0: 0x14c8, 0x22c1: 0x092b, 0x22c2: 0x17f6, 0x22c3: 0x092d, 0x22c4: 0x092f, 0x22c5: 0x155f, - 0x22c6: 0x0931, 0x22c7: 0x1570, 0x22c8: 0x17f8, 0x22c9: 0x14d4, 0x22ca: 0x0007, 0x22cb: 0x093d, - 0x22cc: 0x0984, 0x22cd: 0x093f, 0x22ce: 0x0941, 0x22cf: 0x098c, 0x22d0: 0x094c, 0x22d1: 0x0494, - 0x22d2: 0x097c, 0x22d3: 0x0499, 0x22d4: 0x094e, 0x22d5: 0x04c5, 0x22d6: 0x0950, 0x22d7: 0x14a0, - 0x22d8: 0x001e, 0x22d9: 0x0960, 0x22da: 0x17fa, 0x22db: 0x049b, 0x22dc: 0x02c8, 0x22dd: 0x0962, - 0x22de: 0x0964, 0x22df: 0x096d, 0x22e0: 0x04a6, 0x22e1: 0x04c7, 0x22e2: 0x04a8, 0x22e3: 0x09df, - 0x22e4: 0x3b27, 0x22e5: 0x3b2a, 0x22e8: 0x3b2d, 0x22e9: 0x3b30, - 0x22ea: 0x14eb, 0x22eb: 0x3b33, 0x22ec: 0x3b36, 0x22ed: 0x3b39, 0x22ee: 0x3b3c, 0x22ef: 0x057b, - 0x22f0: 0x3b3f, 0x22f1: 0x3b42, 0x22f2: 0x3b45, 0x22f3: 0x3b48, 0x22f4: 0x3b4b, 0x22f5: 0x3b4e, - 0x22f6: 0x3b51, 0x22f7: 0x14ee, 0x22f8: 0x3b54, 0x22f9: 0x057b, 0x22fa: 0x0581, 0x22fb: 0x3b57, - 0x22fc: 0x055f, 0x22fd: 0x3b5a, 0x22fe: 0x3b5d, 0x22ff: 0x3b60, - // Block 0x8c, offset 0x2300 - 0x2300: 0x14d6, 0x2301: 0x3b63, 0x2302: 0x3b67, 0x2303: 0x0559, 0x2304: 0x0973, 0x2305: 0x0976, - 0x2306: 0x057e, 0x2307: 0x3b6a, 0x2308: 0x3b6d, 0x2309: 0x055c, 0x230a: 0x12fd, 0x230b: 0x0572, - 0x230c: 0x3b70, 0x230d: 0x0015, 0x230e: 0x3b73, 0x230f: 0x3b76, 0x2310: 0x3b79, 0x2311: 0x056f, - 0x2312: 0x0575, 0x2313: 0x0578, 0x2314: 0x3b7c, 0x2315: 0x3b7f, 0x2316: 0x3b82, 0x2317: 0x056c, - 0x2318: 0x0979, 0x2319: 0x3b85, 0x231a: 0x3b88, 0x231b: 0x3b8b, 0x231c: 0x057e, 0x231d: 0x055c, - 0x231e: 0x0572, 0x231f: 0x056c, 0x2320: 0x0575, 0x2321: 0x056f, 0x2322: 0x3b2d, 0x2323: 0x3b30, - 0x2324: 0x14eb, 0x2325: 0x3b33, 0x2326: 0x3b36, 0x2327: 0x3b39, 0x2328: 0x3b3c, 0x2329: 0x057b, - 0x232a: 0x3b3f, 0x232b: 0x3b42, 0x232c: 0x3b45, 0x232d: 0x3b48, 0x232e: 0x3b4b, 0x232f: 0x3b4e, - 0x2330: 0x3b51, 0x2331: 0x14ee, 0x2332: 0x3b54, 0x2333: 0x057b, 0x2334: 0x0581, 0x2335: 0x3b57, - 0x2336: 0x055f, 0x2337: 0x3b5a, 0x2338: 0x3b5d, 0x2339: 0x3b60, 0x233a: 0x14d6, 0x233b: 0x3b63, - 0x233c: 0x3b67, 0x233d: 0x0559, 0x233e: 0x0973, 0x233f: 0x0976, - // Block 0x8d, offset 0x2340 - 0x2340: 0x057e, 0x2341: 0x3b6a, 0x2342: 0x3b6d, 0x2343: 0x055c, 0x2344: 0x12fd, 0x2345: 0x0572, - 0x2346: 0x3b70, 0x2347: 0x0015, 0x2348: 0x3b73, 0x2349: 0x3b76, 0x234a: 0x3b79, 0x234b: 0x056f, - 0x234c: 0x0575, 0x234d: 0x0578, 0x234e: 0x3b7c, 0x234f: 0x3b7f, 0x2350: 0x3b82, 0x2351: 0x056c, - 0x2352: 0x0979, 0x2353: 0x3b85, 0x2354: 0x3b88, 0x2355: 0x3b8b, 0x2356: 0x057e, 0x2357: 0x055c, - 0x2358: 0x0572, 0x2359: 0x056c, 0x235a: 0x0575, 0x235b: 0x056f, 0x235c: 0x3b2d, 0x235d: 0x3b30, - 0x235e: 0x14eb, 0x235f: 0x3b33, 0x2360: 0x3b36, 0x2361: 0x3b39, 0x2362: 0x3b3c, 0x2363: 0x057b, - 0x2364: 0x3b3f, 0x2365: 0x3b42, 0x2366: 0x3b45, 0x2367: 0x3b48, 0x2368: 0x3b4b, 0x2369: 0x3b4e, - 0x236a: 0x3b51, 0x236b: 0x14ee, 0x236c: 0x3b54, 0x236d: 0x057b, 0x236e: 0x0581, 0x236f: 0x3b57, - 0x2370: 0x055f, 0x2371: 0x3b5a, 0x2372: 0x3b5d, 0x2373: 0x3b60, 0x2374: 0x14d6, 0x2375: 0x3b63, - 0x2376: 0x3b67, 0x2377: 0x0559, 0x2378: 0x0973, 0x2379: 0x0976, 0x237a: 0x057e, 0x237b: 0x3b6a, - 0x237c: 0x3b6d, 0x237d: 0x055c, 0x237e: 0x12fd, 0x237f: 0x0572, - // Block 0x8e, offset 0x2380 - 0x2380: 0x3b70, 0x2381: 0x0015, 0x2382: 0x3b73, 0x2383: 0x3b76, 0x2384: 0x3b79, 0x2385: 0x056f, - 0x2386: 0x0575, 0x2387: 0x0578, 0x2388: 0x3b7c, 0x2389: 0x3b7f, 0x238a: 0x3b82, 0x238b: 0x056c, - 0x238c: 0x0979, 0x238d: 0x3b85, 0x238e: 0x3b88, 0x238f: 0x3b8b, 0x2390: 0x057e, 0x2391: 0x055c, - 0x2392: 0x0572, 0x2393: 0x056c, 0x2394: 0x0575, 0x2395: 0x056f, 0x2396: 0x3b2d, 0x2397: 0x3b30, - 0x2398: 0x14eb, 0x2399: 0x3b33, 0x239a: 0x3b36, 0x239b: 0x3b39, 0x239c: 0x3b3c, 0x239d: 0x057b, - 0x239e: 0x3b3f, 0x239f: 0x3b42, 0x23a0: 0x3b45, 0x23a1: 0x3b48, 0x23a2: 0x3b4b, 0x23a3: 0x3b4e, - 0x23a4: 0x3b51, 0x23a5: 0x14ee, 0x23a6: 0x3b54, 0x23a7: 0x057b, 0x23a8: 0x0581, 0x23a9: 0x3b57, - 0x23aa: 0x055f, 0x23ab: 0x3b5a, 0x23ac: 0x3b5d, 0x23ad: 0x3b60, 0x23ae: 0x14d6, 0x23af: 0x3b63, - 0x23b0: 0x3b67, 0x23b1: 0x0559, 0x23b2: 0x0973, 0x23b3: 0x0976, 0x23b4: 0x057e, 0x23b5: 0x3b6a, - 0x23b6: 0x3b6d, 0x23b7: 0x055c, 0x23b8: 0x12fd, 0x23b9: 0x0572, 0x23ba: 0x3b70, 0x23bb: 0x0015, - 0x23bc: 0x3b73, 0x23bd: 0x3b76, 0x23be: 0x3b79, 0x23bf: 0x056f, - // Block 0x8f, offset 0x23c0 - 0x23c0: 0x0575, 0x23c1: 0x0578, 0x23c2: 0x3b7c, 0x23c3: 0x3b7f, 0x23c4: 0x3b82, 0x23c5: 0x056c, - 0x23c6: 0x0979, 0x23c7: 0x3b85, 0x23c8: 0x3b88, 0x23c9: 0x3b8b, 0x23ca: 0x057e, 0x23cb: 0x055c, - 0x23cc: 0x0572, 0x23cd: 0x056c, 0x23ce: 0x0575, 0x23cf: 0x056f, 0x23d0: 0x3b2d, 0x23d1: 0x3b30, - 0x23d2: 0x14eb, 0x23d3: 0x3b33, 0x23d4: 0x3b36, 0x23d5: 0x3b39, 0x23d6: 0x3b3c, 0x23d7: 0x057b, - 0x23d8: 0x3b3f, 0x23d9: 0x3b42, 0x23da: 0x3b45, 0x23db: 0x3b48, 0x23dc: 0x3b4b, 0x23dd: 0x3b4e, - 0x23de: 0x3b51, 0x23df: 0x14ee, 0x23e0: 0x3b54, 0x23e1: 0x057b, 0x23e2: 0x0581, 0x23e3: 0x3b57, - 0x23e4: 0x055f, 0x23e5: 0x3b5a, 0x23e6: 0x3b5d, 0x23e7: 0x3b60, 0x23e8: 0x14d6, 0x23e9: 0x3b63, - 0x23ea: 0x3b67, 0x23eb: 0x0559, 0x23ec: 0x0973, 0x23ed: 0x0976, 0x23ee: 0x057e, 0x23ef: 0x3b6a, - 0x23f0: 0x3b6d, 0x23f1: 0x055c, 0x23f2: 0x12fd, 0x23f3: 0x0572, 0x23f4: 0x3b70, 0x23f5: 0x0015, - 0x23f6: 0x3b73, 0x23f7: 0x3b76, 0x23f8: 0x3b79, 0x23f9: 0x056f, 0x23fa: 0x0575, 0x23fb: 0x0578, - 0x23fc: 0x3b7c, 0x23fd: 0x3b7f, 0x23fe: 0x3b82, 0x23ff: 0x056c, - // Block 0x90, offset 0x2400 - 0x2400: 0x0979, 0x2401: 0x3b85, 0x2402: 0x3b88, 0x2403: 0x3b8b, 0x2404: 0x057e, 0x2405: 0x055c, - 0x2406: 0x0572, 0x2407: 0x056c, 0x2408: 0x0575, 0x2409: 0x056f, 0x240a: 0x3b8f, 0x240b: 0x3b92, - 0x240e: 0x1486, 0x240f: 0x001c, 0x2410: 0x000d, 0x2411: 0x000f, - 0x2412: 0x1488, 0x2413: 0x148a, 0x2414: 0x148c, 0x2415: 0x148e, 0x2416: 0x1490, 0x2417: 0x1492, - 0x2418: 0x1486, 0x2419: 0x001c, 0x241a: 0x000d, 0x241b: 0x000f, 0x241c: 0x1488, 0x241d: 0x148a, - 0x241e: 0x148c, 0x241f: 0x148e, 0x2420: 0x1490, 0x2421: 0x1492, 0x2422: 0x1486, 0x2423: 0x001c, - 0x2424: 0x000d, 0x2425: 0x000f, 0x2426: 0x1488, 0x2427: 0x148a, 0x2428: 0x148c, 0x2429: 0x148e, - 0x242a: 0x1490, 0x242b: 0x1492, 0x242c: 0x1486, 0x242d: 0x001c, 0x242e: 0x000d, 0x242f: 0x000f, - 0x2430: 0x1488, 0x2431: 0x148a, 0x2432: 0x148c, 0x2433: 0x148e, 0x2434: 0x1490, 0x2435: 0x1492, - 0x2436: 0x1486, 0x2437: 0x001c, 0x2438: 0x000d, 0x2439: 0x000f, 0x243a: 0x1488, 0x243b: 0x148a, - 0x243c: 0x148c, 0x243d: 0x148e, 0x243e: 0x1490, 0x243f: 0x1492, - // Block 0x91, offset 0x2440 - 0x2440: 0x3b95, 0x2441: 0x3b98, 0x2442: 0x3b9b, 0x2443: 0x3b9e, 0x2444: 0x3ba1, 0x2445: 0x3ba4, - 0x2446: 0x3ba7, 0x2447: 0x3baa, 0x2448: 0x3bad, 0x2449: 0x3bb0, 0x244a: 0x3bb3, - 0x2450: 0x3bb6, 0x2451: 0x3bba, - 0x2452: 0x3bbe, 0x2453: 0x3bc2, 0x2454: 0x3bc6, 0x2455: 0x3bca, 0x2456: 0x3bce, 0x2457: 0x3bd2, - 0x2458: 0x3bd6, 0x2459: 0x3bda, 0x245a: 0x3bde, 0x245b: 0x3be2, 0x245c: 0x3be6, 0x245d: 0x3bea, - 0x245e: 0x3bee, 0x245f: 0x3bf2, 0x2460: 0x3bf6, 0x2461: 0x3bfa, 0x2462: 0x3bfe, 0x2463: 0x3c02, - 0x2464: 0x3c06, 0x2465: 0x3c0a, 0x2466: 0x3c0e, 0x2467: 0x3c12, 0x2468: 0x3c16, 0x2469: 0x3c1a, - 0x246a: 0x3c1e, 0x246b: 0x14ad, 0x246c: 0x092b, 0x246d: 0x3c26, 0x246e: 0x3c29, - 0x2470: 0x0906, 0x2471: 0x090b, 0x2472: 0x14ad, 0x2473: 0x090d, 0x2474: 0x090f, 0x2475: 0x14d9, - 0x2476: 0x0914, 0x2477: 0x0916, 0x2478: 0x0918, 0x2479: 0x091a, 0x247a: 0x091c, 0x247b: 0x091e, - 0x247c: 0x0920, 0x247d: 0x0922, 0x247e: 0x0924, 0x247f: 0x0929, - // Block 0x92, offset 0x2480 - 0x2480: 0x14c8, 0x2481: 0x092b, 0x2482: 0x17f6, 0x2483: 0x092d, 0x2484: 0x092f, 0x2485: 0x155f, - 0x2486: 0x0931, 0x2487: 0x1570, 0x2488: 0x17f8, 0x2489: 0x14d4, 0x248a: 0x3c2c, 0x248b: 0x293d, - 0x248c: 0x3c2f, 0x248d: 0x3c32, 0x248e: 0x3c35, 0x248f: 0x3c39, - // Block 0x93, offset 0x24c0 - 0x24d0: 0x3c3c, - // Block 0x94, offset 0x2500 - 0x2500: 0x3c3f, 0x2501: 0x3c46, 0x2502: 0x2291, - 0x2510: 0x1922, 0x2511: 0x3c4d, - 0x2512: 0x3c51, 0x2513: 0x1cb3, 0x2514: 0x183e, 0x2515: 0x3c55, 0x2516: 0x3c59, 0x2517: 0x1ed0, - 0x2518: 0x3c5d, 0x2519: 0x3c61, 0x251a: 0x3c65, 0x251b: 0x2d49, 0x251c: 0x3c69, 0x251d: 0x3c6d, - 0x251e: 0x3c71, 0x251f: 0x3c75, 0x2520: 0x3c79, 0x2521: 0x3c7d, 0x2522: 0x19b2, 0x2523: 0x3c81, - 0x2524: 0x3c85, 0x2525: 0x3c89, 0x2526: 0x3c8d, 0x2527: 0x3c91, 0x2528: 0x3c95, 0x2529: 0x1826, - 0x252a: 0x1eb0, 0x252b: 0x3c99, 0x252c: 0x21c7, 0x252d: 0x1ebc, 0x252e: 0x21cb, 0x252f: 0x3c9d, - 0x2530: 0x1a92, 0x2531: 0x3ca1, 0x2532: 0x3ca5, 0x2533: 0x3ca9, 0x2534: 0x3cad, 0x2535: 0x3cb1, - 0x2536: 0x2183, 0x2537: 0x194a, 0x2538: 0x3cb5, 0x2539: 0x3cb9, 0x253a: 0x3cbd, - // Block 0x95, offset 0x2540 - 0x2540: 0x3cc1, 0x2541: 0x3ccb, 0x2542: 0x3cd5, 0x2543: 0x3cdf, 0x2544: 0x3ce9, 0x2545: 0x3cf3, - 0x2546: 0x3cfd, 0x2547: 0x3d07, 0x2548: 0x3d11, - 0x2550: 0x3d1b, 0x2551: 0x3d1f, - // Block 0x96, offset 0x2580 - 0x2580: 0x3d23, 0x2581: 0x3d27, 0x2582: 0x3d2b, 0x2583: 0x3d2f, 0x2584: 0x3d34, 0x2585: 0x2eb5, - 0x2586: 0x3d38, 0x2587: 0x3d3c, 0x2588: 0x3d40, 0x2589: 0x3d44, 0x258a: 0x2eb9, 0x258b: 0x3d48, - 0x258c: 0x3d4c, 0x258d: 0x3d50, 0x258e: 0x2ebd, 0x258f: 0x3d55, 0x2590: 0x3d59, 0x2591: 0x3d5d, - 0x2592: 0x3d61, 0x2593: 0x3d66, 0x2594: 0x3d6a, 0x2595: 0x3c71, 0x2596: 0x3d6e, 0x2597: 0x3d73, - 0x2598: 0x3d77, 0x2599: 0x3d7b, 0x259a: 0x3d7f, 0x259b: 0x2f9a, 0x259c: 0x3d83, 0x259d: 0x1866, - 0x259e: 0x3d88, 0x259f: 0x3d8c, 0x25a0: 0x3d90, 0x25a1: 0x3d94, 0x25a2: 0x3cb9, 0x25a3: 0x3d98, - 0x25a4: 0x3d9c, 0x25a5: 0x2fae, 0x25a6: 0x2ec1, 0x25a7: 0x2ec5, 0x25a8: 0x2fb2, 0x25a9: 0x3da0, - 0x25aa: 0x3da4, 0x25ab: 0x2bf1, 0x25ac: 0x3da8, 0x25ad: 0x2ec9, 0x25ae: 0x3dac, 0x25af: 0x3db0, - 0x25b0: 0x3db4, 0x25b1: 0x3db8, 0x25b2: 0x3db8, 0x25b3: 0x3db8, 0x25b4: 0x3dbc, 0x25b5: 0x3dc1, - 0x25b6: 0x3dc5, 0x25b7: 0x3dc9, 0x25b8: 0x3dcd, 0x25b9: 0x3dd2, 0x25ba: 0x3dd6, 0x25bb: 0x3dda, - 0x25bc: 0x3dde, 0x25bd: 0x3de2, 0x25be: 0x3de6, 0x25bf: 0x3dea, - // Block 0x97, offset 0x25c0 - 0x25c0: 0x3dee, 0x25c1: 0x3df2, 0x25c2: 0x3df6, 0x25c3: 0x3dfa, 0x25c4: 0x3dfe, 0x25c5: 0x3e02, - 0x25c6: 0x3e02, 0x25c7: 0x2fba, 0x25c8: 0x3e06, 0x25c9: 0x3e0a, 0x25ca: 0x3e0e, 0x25cb: 0x3e12, - 0x25cc: 0x2ed1, 0x25cd: 0x3e16, 0x25ce: 0x3e1a, 0x25cf: 0x3e1e, 0x25d0: 0x2e39, 0x25d1: 0x3e22, - 0x25d2: 0x3e26, 0x25d3: 0x3e2a, 0x25d4: 0x3e2e, 0x25d5: 0x3e32, 0x25d6: 0x3e36, 0x25d7: 0x3e3a, - 0x25d8: 0x3e3e, 0x25d9: 0x3e42, 0x25da: 0x3e47, 0x25db: 0x3e4b, 0x25dc: 0x3e4f, 0x25dd: 0x3c55, - 0x25de: 0x3e53, 0x25df: 0x3e57, 0x25e0: 0x3e5b, 0x25e1: 0x3e60, 0x25e2: 0x3e65, 0x25e3: 0x3e69, - 0x25e4: 0x3e6d, 0x25e5: 0x3e71, 0x25e6: 0x3e75, 0x25e7: 0x3e79, 0x25e8: 0x3e7d, 0x25e9: 0x3e81, - 0x25ea: 0x3e85, 0x25eb: 0x3e85, 0x25ec: 0x3e89, 0x25ed: 0x3e8e, 0x25ee: 0x3e92, 0x25ef: 0x2be1, - 0x25f0: 0x3e96, 0x25f1: 0x3e9a, 0x25f2: 0x3e9f, 0x25f3: 0x3ea3, 0x25f4: 0x3ea7, 0x25f5: 0x18ce, - 0x25f6: 0x3eab, 0x25f7: 0x3eaf, 0x25f8: 0x18d6, 0x25f9: 0x3eb3, 0x25fa: 0x3eb7, 0x25fb: 0x3ebb, - 0x25fc: 0x3ec0, 0x25fd: 0x3ec4, 0x25fe: 0x3ec9, 0x25ff: 0x3ecd, - // Block 0x98, offset 0x2600 - 0x2600: 0x3ed1, 0x2601: 0x3ed5, 0x2602: 0x3ed9, 0x2603: 0x3edd, 0x2604: 0x3ee1, 0x2605: 0x3ee5, - 0x2606: 0x3ee9, 0x2607: 0x3eed, 0x2608: 0x3ef1, 0x2609: 0x3ef5, 0x260a: 0x3efa, 0x260b: 0x3efe, - 0x260c: 0x3f02, 0x260d: 0x3f06, 0x260e: 0x2b11, 0x260f: 0x3f0a, 0x2610: 0x18fe, 0x2611: 0x3f0f, - 0x2612: 0x3f0f, 0x2613: 0x3f14, 0x2614: 0x3f18, 0x2615: 0x3f18, 0x2616: 0x3f1c, 0x2617: 0x3f20, - 0x2618: 0x3f25, 0x2619: 0x3f2a, 0x261a: 0x3f2e, 0x261b: 0x3f32, 0x261c: 0x3f36, 0x261d: 0x3f3a, - 0x261e: 0x3f3e, 0x261f: 0x3f42, 0x2620: 0x3f46, 0x2621: 0x3f4a, 0x2622: 0x3f4e, 0x2623: 0x2ee5, - 0x2624: 0x3f52, 0x2625: 0x3f57, 0x2626: 0x3f5b, 0x2627: 0x3f5f, 0x2628: 0x2fea, 0x2629: 0x3f5f, - 0x262a: 0x3f63, 0x262b: 0x2eed, 0x262c: 0x3f67, 0x262d: 0x3f6b, 0x262e: 0x3f6f, 0x262f: 0x3f73, - 0x2630: 0x2ef1, 0x2631: 0x2aa5, 0x2632: 0x3f77, 0x2633: 0x3f7b, 0x2634: 0x3f7f, 0x2635: 0x3f83, - 0x2636: 0x3f87, 0x2637: 0x3f8b, 0x2638: 0x3f8f, 0x2639: 0x3f94, 0x263a: 0x3f98, 0x263b: 0x3f9c, - 0x263c: 0x3fa0, 0x263d: 0x3fa4, 0x263e: 0x3fa8, 0x263f: 0x3fad, - // Block 0x99, offset 0x2640 - 0x2640: 0x3fb1, 0x2641: 0x3fb5, 0x2642: 0x3fb9, 0x2643: 0x3fbd, 0x2644: 0x3fc1, 0x2645: 0x3fc5, - 0x2646: 0x3fc9, 0x2647: 0x3fcd, 0x2648: 0x2ef5, 0x2649: 0x3fd1, 0x264a: 0x3fd5, 0x264b: 0x3fda, - 0x264c: 0x3fde, 0x264d: 0x3fe2, 0x264e: 0x3fe6, 0x264f: 0x2efd, 0x2650: 0x3fea, 0x2651: 0x3fee, - 0x2652: 0x3ff2, 0x2653: 0x3ff6, 0x2654: 0x3ffa, 0x2655: 0x3ffe, 0x2656: 0x4002, 0x2657: 0x4006, - 0x2658: 0x2b15, 0x2659: 0x300a, 0x265a: 0x400a, 0x265b: 0x400e, 0x265c: 0x4012, 0x265d: 0x4016, - 0x265e: 0x401b, 0x265f: 0x401f, 0x2660: 0x4023, 0x2661: 0x4027, 0x2662: 0x2f01, 0x2663: 0x402b, - 0x2664: 0x4030, 0x2665: 0x4034, 0x2666: 0x4038, 0x2667: 0x30b5, 0x2668: 0x403c, 0x2669: 0x4040, - 0x266a: 0x4044, 0x266b: 0x4048, 0x266c: 0x404c, 0x266d: 0x4051, 0x266e: 0x4055, 0x266f: 0x4059, - 0x2670: 0x405d, 0x2671: 0x4062, 0x2672: 0x4066, 0x2673: 0x406a, 0x2674: 0x406e, 0x2675: 0x2c25, - 0x2676: 0x4072, 0x2677: 0x4076, 0x2678: 0x407b, 0x2679: 0x4080, 0x267a: 0x4085, 0x267b: 0x4089, - 0x267c: 0x408e, 0x267d: 0x4092, 0x267e: 0x4096, 0x267f: 0x409a, - // Block 0x9a, offset 0x2680 - 0x2680: 0x409e, 0x2681: 0x2f05, 0x2682: 0x2d71, 0x2683: 0x40a2, 0x2684: 0x40a6, 0x2685: 0x40aa, - 0x2686: 0x40ae, 0x2687: 0x40b3, 0x2688: 0x40b7, 0x2689: 0x40bb, 0x268a: 0x40bf, 0x268b: 0x3016, - 0x268c: 0x40c3, 0x268d: 0x40c7, 0x268e: 0x40cc, 0x268f: 0x40d0, 0x2690: 0x40d4, 0x2691: 0x40d9, - 0x2692: 0x40de, 0x2693: 0x40e2, 0x2694: 0x301a, 0x2695: 0x40e6, 0x2696: 0x40ea, 0x2697: 0x40ee, - 0x2698: 0x40f2, 0x2699: 0x40f6, 0x269a: 0x40fa, 0x269b: 0x40fe, 0x269c: 0x4103, 0x269d: 0x4107, - 0x269e: 0x410c, 0x269f: 0x4110, 0x26a0: 0x4115, 0x26a1: 0x3022, 0x26a2: 0x4119, 0x26a3: 0x411d, - 0x26a4: 0x4122, 0x26a5: 0x4126, 0x26a6: 0x412a, 0x26a7: 0x412f, 0x26a8: 0x4134, 0x26a9: 0x4138, - 0x26aa: 0x413c, 0x26ab: 0x4140, 0x26ac: 0x4144, 0x26ad: 0x4144, 0x26ae: 0x4148, 0x26af: 0x414c, - 0x26b0: 0x302a, 0x26b1: 0x4150, 0x26b2: 0x4154, 0x26b3: 0x4158, 0x26b4: 0x415c, 0x26b5: 0x4160, - 0x26b6: 0x4165, 0x26b7: 0x4169, 0x26b8: 0x2bed, 0x26b9: 0x416e, 0x26ba: 0x4173, 0x26bb: 0x4177, - 0x26bc: 0x417c, 0x26bd: 0x4181, 0x26be: 0x4186, 0x26bf: 0x418a, - // Block 0x9b, offset 0x26c0 - 0x26c0: 0x3042, 0x26c1: 0x418e, 0x26c2: 0x4193, 0x26c3: 0x4198, 0x26c4: 0x419d, 0x26c5: 0x41a2, - 0x26c6: 0x41a6, 0x26c7: 0x41a6, 0x26c8: 0x3046, 0x26c9: 0x30bd, 0x26ca: 0x41aa, 0x26cb: 0x41ae, - 0x26cc: 0x41b2, 0x26cd: 0x41b6, 0x26ce: 0x41bb, 0x26cf: 0x2b59, 0x26d0: 0x304e, 0x26d1: 0x41bf, - 0x26d2: 0x41c3, 0x26d3: 0x2f2d, 0x26d4: 0x41c8, 0x26d5: 0x41cd, 0x26d6: 0x2e89, 0x26d7: 0x41d2, - 0x26d8: 0x41d6, 0x26d9: 0x2f39, 0x26da: 0x41da, 0x26db: 0x41de, 0x26dc: 0x41e2, 0x26dd: 0x41e7, - 0x26de: 0x41e7, 0x26df: 0x41ec, 0x26e0: 0x41f0, 0x26e1: 0x41f4, 0x26e2: 0x41f9, 0x26e3: 0x41fd, - 0x26e4: 0x4201, 0x26e5: 0x4205, 0x26e6: 0x420a, 0x26e7: 0x420e, 0x26e8: 0x4212, 0x26e9: 0x4216, - 0x26ea: 0x421a, 0x26eb: 0x421e, 0x26ec: 0x4223, 0x26ed: 0x4227, 0x26ee: 0x422b, 0x26ef: 0x422f, - 0x26f0: 0x4233, 0x26f1: 0x4237, 0x26f2: 0x423b, 0x26f3: 0x4240, 0x26f4: 0x4245, 0x26f5: 0x4249, - 0x26f6: 0x424e, 0x26f7: 0x4252, 0x26f8: 0x4257, 0x26f9: 0x425b, 0x26fa: 0x2f51, 0x26fb: 0x425f, - 0x26fc: 0x4264, 0x26fd: 0x4269, 0x26fe: 0x426d, 0x26ff: 0x4272, - // Block 0x9c, offset 0x2700 - 0x2700: 0x4276, 0x2701: 0x427b, 0x2702: 0x427f, 0x2703: 0x4283, 0x2704: 0x4287, 0x2705: 0x428b, - 0x2706: 0x428f, 0x2707: 0x4293, 0x2708: 0x4298, 0x2709: 0x429d, 0x270a: 0x42a2, 0x270b: 0x3f14, - 0x270c: 0x42a7, 0x270d: 0x42ab, 0x270e: 0x42af, 0x270f: 0x42b3, 0x2710: 0x42b7, 0x2711: 0x42bb, - 0x2712: 0x42bf, 0x2713: 0x42c3, 0x2714: 0x42c7, 0x2715: 0x42cb, 0x2716: 0x42cf, 0x2717: 0x42d3, - 0x2718: 0x2c31, 0x2719: 0x42d8, 0x271a: 0x42dc, 0x271b: 0x42e0, 0x271c: 0x42e4, 0x271d: 0x42e8, - 0x271e: 0x42ec, 0x271f: 0x2f5d, 0x2720: 0x42f0, 0x2721: 0x42f4, 0x2722: 0x42f8, 0x2723: 0x42fc, - 0x2724: 0x4300, 0x2725: 0x4305, 0x2726: 0x430a, 0x2727: 0x430f, 0x2728: 0x4313, 0x2729: 0x4317, - 0x272a: 0x431b, 0x272b: 0x431f, 0x272c: 0x4324, 0x272d: 0x4328, 0x272e: 0x432d, 0x272f: 0x4331, - 0x2730: 0x4335, 0x2731: 0x433a, 0x2732: 0x433f, 0x2733: 0x4343, 0x2734: 0x2b45, 0x2735: 0x4347, - 0x2736: 0x434b, 0x2737: 0x434f, 0x2738: 0x4353, 0x2739: 0x4357, 0x273a: 0x435b, 0x273b: 0x306a, - 0x273c: 0x435f, 0x273d: 0x4363, 0x273e: 0x4367, 0x273f: 0x436b, - // Block 0x9d, offset 0x2740 - 0x2740: 0x436f, 0x2741: 0x4373, 0x2742: 0x4377, 0x2743: 0x437b, 0x2744: 0x1a66, 0x2745: 0x437f, - 0x2746: 0x4384, 0x2747: 0x4388, 0x2748: 0x438c, 0x2749: 0x4390, 0x274a: 0x4394, 0x274b: 0x4398, - 0x274c: 0x439d, 0x274d: 0x43a2, 0x274e: 0x43a6, 0x274f: 0x43aa, 0x2750: 0x307e, 0x2751: 0x3082, - 0x2752: 0x1a82, 0x2753: 0x43ae, 0x2754: 0x43b3, 0x2755: 0x43b7, 0x2756: 0x43bb, 0x2757: 0x43bf, - 0x2758: 0x43c3, 0x2759: 0x43c8, 0x275a: 0x43cd, 0x275b: 0x43d1, 0x275c: 0x43d5, 0x275d: 0x43d9, - 0x275e: 0x43de, 0x275f: 0x3086, 0x2760: 0x43e2, 0x2761: 0x43e7, 0x2762: 0x43ec, 0x2763: 0x43f0, - 0x2764: 0x43f4, 0x2765: 0x43f8, 0x2766: 0x43fd, 0x2767: 0x4401, 0x2768: 0x4405, 0x2769: 0x4409, - 0x276a: 0x440d, 0x276b: 0x4411, 0x276c: 0x4415, 0x276d: 0x4419, 0x276e: 0x441e, 0x276f: 0x4422, - 0x2770: 0x4426, 0x2771: 0x442a, 0x2772: 0x442f, 0x2773: 0x4433, 0x2774: 0x4437, 0x2775: 0x443b, - 0x2776: 0x443f, 0x2777: 0x4444, 0x2778: 0x4449, 0x2779: 0x444d, 0x277a: 0x4451, 0x277b: 0x4455, - 0x277c: 0x445a, 0x277d: 0x445e, 0x277e: 0x309e, 0x277f: 0x309e, - // Block 0x9e, offset 0x2780 - 0x2780: 0x4463, 0x2781: 0x4467, 0x2782: 0x446c, 0x2783: 0x4470, 0x2784: 0x4474, 0x2785: 0x4478, - 0x2786: 0x447c, 0x2787: 0x4480, 0x2788: 0x4484, 0x2789: 0x4488, 0x278a: 0x30a2, 0x278b: 0x448d, - 0x278c: 0x4491, 0x278d: 0x4495, 0x278e: 0x4499, 0x278f: 0x449d, 0x2790: 0x44a1, 0x2791: 0x44a6, - 0x2792: 0x44aa, 0x2793: 0x44af, 0x2794: 0x44b4, 0x2795: 0x1b42, 0x2796: 0x44b9, 0x2797: 0x1b52, - 0x2798: 0x44bd, 0x2799: 0x44c1, 0x279a: 0x44c5, 0x279b: 0x44c9, 0x279c: 0x1b66, 0x279d: 0x44cd, + 0x1040: 0x436f, 0x1041: 0x4373, 0x1042: 0x4377, 0x1043: 0x437b, 0x1044: 0x1a66, 0x1045: 0x437f, + 0x1046: 0x4384, 0x1047: 0x4388, 0x1048: 0x438c, 0x1049: 0x4390, 0x104a: 0x4394, 0x104b: 0x4398, + 0x104c: 0x439d, 0x104d: 0x43a2, 0x104e: 0x43a6, 0x104f: 0x43aa, 0x1050: 0x307e, 0x1051: 0x3082, + 0x1052: 0x1a82, 0x1053: 0x43ae, 0x1054: 0x43b3, 0x1055: 0x43b7, 0x1056: 0x43bb, 0x1057: 0x43bf, + 0x1058: 0x43c3, 0x1059: 0x43c8, 0x105a: 0x43cd, 0x105b: 0x43d1, 0x105c: 0x43d5, 0x105d: 0x43d9, + 0x105e: 0x43de, 0x105f: 0x3086, 0x1060: 0x43e2, 0x1061: 0x43e7, 0x1062: 0x43ec, 0x1063: 0x43f0, + 0x1064: 0x43f4, 0x1065: 0x43f8, 0x1066: 0x43fd, 0x1067: 0x4401, 0x1068: 0x4405, 0x1069: 0x4409, + 0x106a: 0x440d, 0x106b: 0x4411, 0x106c: 0x4415, 0x106d: 0x4419, 0x106e: 0x441e, 0x106f: 0x4422, + 0x1070: 0x4426, 0x1071: 0x442a, 0x1072: 0x442f, 0x1073: 0x4433, 0x1074: 0x4437, 0x1075: 0x443b, + 0x1076: 0x443f, 0x1077: 0x4444, 0x1078: 0x4449, 0x1079: 0x444d, 0x107a: 0x4451, 0x107b: 0x4455, + 0x107c: 0x445a, 0x107d: 0x445e, 0x107e: 0x309e, 0x107f: 0x309e, +} + +// nfkcDecompSparseOffset: 93 entries, 186 bytes +var nfkcDecompSparseOffset = []uint16{0x0, 0xc, 0x16, 0x1e, 0x24, 0x27, 0x31, 0x37, 0x3e, 0x44, 0x4c, 0x59, 0x60, 0x66, 0x6e, 0x70, 0x72, 0x74, 0x78, 0x7c, 0x7e, 0x82, 0x85, 0x88, 0x8c, 0x8e, 0x90, 0x92, 0x96, 0x98, 0x9c, 0x9e, 0xa0, 0xa2, 0xa4, 0xae, 0xb6, 0xb8, 0xba, 0xc3, 0xc6, 0xcd, 0xd8, 0xe6, 0xf4, 0xfe, 0x102, 0x104, 0x10e, 0x11a, 0x11f, 0x122, 0x124, 0x126, 0x129, 0x12b, 0x12d, 0x12f, 0x131, 0x133, 0x135, 0x137, 0x139, 0x13b, 0x140, 0x14f, 0x15d, 0x15f, 0x161, 0x169, 0x179, 0x17b, 0x186, 0x18d, 0x198, 0x1a4, 0x1b5, 0x1c6, 0x1cd, 0x1de, 0x1ec, 0x1fa, 0x209, 0x21a, 0x21f, 0x22c, 0x230, 0x234, 0x238, 0x23a, 0x249, 0x24b, 0x24f} + +// nfkcDecompSparseValues: 605 entries, 2420 bytes +var nfkcDecompSparseValues = [605]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0002, lo: 0x0b}, + {value: 0x0001, lo: 0xa0, hi: 0xa0}, + {value: 0x0003, lo: 0xa8, hi: 0xa8}, + {value: 0x0007, lo: 0xaa, hi: 0xaa}, + {value: 0x0009, lo: 0xaf, hi: 0xaf}, + {value: 0x000d, lo: 0xb2, hi: 0xb4}, + {value: 0x0015, lo: 0xb5, hi: 0xb5}, + {value: 0x0018, lo: 0xb8, hi: 0xb8}, + {value: 0x001c, lo: 0xb9, hi: 0xba}, + {value: 0x0020, lo: 0xbc, hi: 0xbc}, + {value: 0x0026, lo: 0xbd, hi: 0xbd}, + {value: 0x002c, lo: 0xbe, hi: 0xbe}, + // Block 0x1, offset 0x2 + {value: 0x0004, lo: 0x09}, + {value: 0x0032, lo: 0x80, hi: 0x85}, + {value: 0x004a, lo: 0x87, hi: 0x8f}, + {value: 0x006e, lo: 0x91, hi: 0x96}, + {value: 0x0086, lo: 0x99, hi: 0x9d}, + {value: 0x009a, lo: 0xa0, hi: 0xa5}, + {value: 0x00b2, lo: 0xa7, hi: 0xaf}, + {value: 0x00d6, lo: 0xb1, hi: 0xb6}, + {value: 0x00ee, lo: 0xb9, hi: 0xbd}, + {value: 0x0102, lo: 0xbf, hi: 0xbf}, + // Block 0x2, offset 0x3 + {value: 0x0004, lo: 0x07}, + {value: 0x0106, lo: 0x80, hi: 0x8f}, + {value: 0x0146, lo: 0x92, hi: 0xa5}, + {value: 0x0196, lo: 0xa8, hi: 0xb0}, + {value: 0x01ba, lo: 0xb2, hi: 0xb2}, + {value: 0x01bd, lo: 0xb3, hi: 0xb3}, + {value: 0x01c0, lo: 0xb4, hi: 0xb7}, + {value: 0x01d0, lo: 0xb9, hi: 0xbf}, + // Block 0x3, offset 0x4 + {value: 0x0004, lo: 0x05}, + {value: 0x01ec, lo: 0x80, hi: 0x80}, + {value: 0x01f0, lo: 0x83, hi: 0x89}, + {value: 0x020c, lo: 0x8c, hi: 0x91}, + {value: 0x0224, lo: 0x94, hi: 0xa5}, + {value: 0x026c, lo: 0xa8, hi: 0xbf}, + // Block 0x4, offset 0x5 + {value: 0x0004, lo: 0x02}, + {value: 0x02ca, lo: 0xa0, hi: 0xa1}, + {value: 0x02d2, lo: 0xaf, hi: 0xb0}, + // Block 0x5, offset 0x6 + {value: 0x0004, lo: 0x09}, + {value: 0x03d8, lo: 0x80, hi: 0x9b}, + {value: 0x0448, lo: 0x9e, hi: 0x9f}, + {value: 0x0450, lo: 0xa6, hi: 0xaa}, + {value: 0x0466, lo: 0xab, hi: 0xab}, + {value: 0x046c, lo: 0xac, hi: 0xac}, + {value: 0x0472, lo: 0xad, hi: 0xad}, + {value: 0x0478, lo: 0xae, hi: 0xb0}, + {value: 0x0486, lo: 0xb1, hi: 0xb1}, + {value: 0x048c, lo: 0xb2, hi: 0xb3}, + // Block 0x6, offset 0x7 + {value: 0x0002, lo: 0x05}, + {value: 0x0494, lo: 0xb0, hi: 0xb1}, + {value: 0x0499, lo: 0xb2, hi: 0xb4}, + {value: 0x04a0, lo: 0xb5, hi: 0xb5}, + {value: 0x04a3, lo: 0xb6, hi: 0xb6}, + {value: 0x04a6, lo: 0xb7, hi: 0xb8}, + // Block 0x7, offset 0x8 + {value: 0x0004, lo: 0x06}, + {value: 0x04aa, lo: 0x98, hi: 0x9d}, + {value: 0x04c2, lo: 0xa0, hi: 0xa0}, + {value: 0x04c5, lo: 0xa1, hi: 0xa1}, + {value: 0x02c8, lo: 0xa2, hi: 0xa2}, + {value: 0x04c7, lo: 0xa3, hi: 0xa3}, + {value: 0x04c9, lo: 0xa4, hi: 0xa4}, + // Block 0x8, offset 0x9 + {value: 0x0003, lo: 0x05}, + {value: 0x04cc, lo: 0x80, hi: 0x81}, + {value: 0x04d2, lo: 0x83, hi: 0x84}, + {value: 0x04da, lo: 0xb4, hi: 0xb4}, + {value: 0x04dd, lo: 0xba, hi: 0xba}, + {value: 0x04e1, lo: 0xbe, hi: 0xbe}, + // Block 0x9, offset 0xa + {value: 0x0005, lo: 0x07}, + {value: 0x0011, lo: 0x84, hi: 0x84}, + {value: 0x04e8, lo: 0x85, hi: 0x85}, + {value: 0x04ee, lo: 0x86, hi: 0x87}, + {value: 0x04f6, lo: 0x88, hi: 0x8a}, + {value: 0x0505, lo: 0x8c, hi: 0x8c}, + {value: 0x050a, lo: 0x8e, hi: 0x90}, + {value: 0x051b, lo: 0xaa, hi: 0xb0}, + // Block 0xa, offset 0xb + {value: 0x0003, lo: 0x0c}, + {value: 0x0540, lo: 0x8a, hi: 0x8a}, + {value: 0x0545, lo: 0x8b, hi: 0x8b}, + {value: 0x054a, lo: 0x8c, hi: 0x8c}, + {value: 0x054f, lo: 0x8d, hi: 0x8d}, + {value: 0x0554, lo: 0x8e, hi: 0x8e}, + {value: 0x0559, lo: 0x90, hi: 0x92}, + {value: 0x050a, lo: 0x93, hi: 0x93}, + {value: 0x0520, lo: 0x94, hi: 0x94}, + {value: 0x056c, lo: 0x95, hi: 0x96}, + {value: 0x0572, lo: 0xb0, hi: 0xb2}, + {value: 0x057b, lo: 0xb4, hi: 0xb5}, + {value: 0x0581, lo: 0xb9, hi: 0xb9}, + // Block 0xb, offset 0xc + {value: 0x0005, lo: 0x06}, + {value: 0x0584, lo: 0x80, hi: 0x81}, + {value: 0x058e, lo: 0x83, hi: 0x83}, + {value: 0x0593, lo: 0x87, hi: 0x87}, + {value: 0x0598, lo: 0x8c, hi: 0x8e}, + {value: 0x05a7, lo: 0x99, hi: 0x99}, + {value: 0x05ac, lo: 0xb9, hi: 0xb9}, + // Block 0xc, offset 0xd + {value: 0x0005, lo: 0x05}, + {value: 0x05b1, lo: 0x90, hi: 0x91}, + {value: 0x05bb, lo: 0x93, hi: 0x93}, + {value: 0x05c0, lo: 0x97, hi: 0x97}, + {value: 0x05c5, lo: 0x9c, hi: 0x9e}, + {value: 0x05d4, lo: 0xb6, hi: 0xb7}, + // Block 0xd, offset 0xe + {value: 0x0005, lo: 0x07}, + {value: 0x05de, lo: 0x81, hi: 0x82}, + {value: 0x05e8, lo: 0x90, hi: 0x93}, + {value: 0x05fc, lo: 0x96, hi: 0x97}, + {value: 0x0606, lo: 0x9a, hi: 0x9f}, + {value: 0x0624, lo: 0xa2, hi: 0xa7}, + {value: 0x0642, lo: 0xaa, hi: 0xb5}, + {value: 0x067e, lo: 0xb8, hi: 0xb9}, + // Block 0xe, offset 0xf + {value: 0x0000, lo: 0x01}, + {value: 0x0688, lo: 0x87, hi: 0x87}, + // Block 0xf, offset 0x10 + {value: 0x0005, lo: 0x01}, + {value: 0x068d, lo: 0xa2, hi: 0xa6}, + // Block 0x10, offset 0x11 + {value: 0x0005, lo: 0x01}, + {value: 0x06a6, lo: 0xb5, hi: 0xb8}, + // Block 0x11, offset 0x12 + {value: 0x0005, lo: 0x03}, + {value: 0x06ba, lo: 0x80, hi: 0x80}, + {value: 0x06bf, lo: 0x82, hi: 0x82}, + {value: 0x06c4, lo: 0x93, hi: 0x93}, + // Block 0x12, offset 0x13 + {value: 0x0007, lo: 0x03}, + {value: 0x06c9, lo: 0xa9, hi: 0xa9}, + {value: 0x06d0, lo: 0xb1, hi: 0xb1}, + {value: 0x06d7, lo: 0xb4, hi: 0xb4}, + // Block 0x13, offset 0x14 + {value: 0x0007, lo: 0x01}, + {value: 0x06de, lo: 0x98, hi: 0x9f}, + // Block 0x14, offset 0x15 + {value: 0x0007, lo: 0x03}, + {value: 0x0716, lo: 0x8b, hi: 0x8c}, + {value: 0x0724, lo: 0x9c, hi: 0x9d}, + {value: 0x0732, lo: 0x9f, hi: 0x9f}, + // Block 0x15, offset 0x16 + {value: 0x0007, lo: 0x02}, + {value: 0x0739, lo: 0xb3, hi: 0xb3}, + {value: 0x0740, lo: 0xb6, hi: 0xb6}, + // Block 0x16, offset 0x17 + {value: 0x0007, lo: 0x02}, + {value: 0x0747, lo: 0x99, hi: 0x9b}, + {value: 0x075c, lo: 0x9e, hi: 0x9e}, + // Block 0x17, offset 0x18 + {value: 0x0007, lo: 0x03}, + {value: 0x0763, lo: 0x88, hi: 0x88}, + {value: 0x076a, lo: 0x8b, hi: 0x8c}, + {value: 0x0778, lo: 0x9c, hi: 0x9d}, + // Block 0x18, offset 0x19 + {value: 0x0000, lo: 0x01}, + {value: 0x0786, lo: 0x94, hi: 0x94}, + // Block 0x19, offset 0x1a + {value: 0x0007, lo: 0x01}, + {value: 0x078d, lo: 0x8a, hi: 0x8c}, + // Block 0x1a, offset 0x1b + {value: 0x0000, lo: 0x01}, + {value: 0x07a2, lo: 0x88, hi: 0x88}, + // Block 0x1b, offset 0x1c + {value: 0x0007, lo: 0x03}, + {value: 0x07a9, lo: 0x80, hi: 0x80}, + {value: 0x07b0, lo: 0x87, hi: 0x88}, + {value: 0x07be, lo: 0x8a, hi: 0x8b}, + // Block 0x1c, offset 0x1d + {value: 0x0007, lo: 0x01}, + {value: 0x07cf, lo: 0x8a, hi: 0x8c}, + // Block 0x1d, offset 0x1e + {value: 0x0007, lo: 0x03}, + {value: 0x07e4, lo: 0x9a, hi: 0x9a}, + {value: 0x07eb, lo: 0x9c, hi: 0x9d}, + {value: 0x07fc, lo: 0x9e, hi: 0x9e}, + // Block 0x1e, offset 0x1f + {value: 0x0000, lo: 0x01}, + {value: 0x0803, lo: 0xb3, hi: 0xb3}, + // Block 0x1f, offset 0x20 + {value: 0x0000, lo: 0x01}, + {value: 0x080a, lo: 0xb3, hi: 0xb3}, + // Block 0x20, offset 0x21 + {value: 0x0007, lo: 0x01}, + {value: 0x0811, lo: 0x9c, hi: 0x9d}, + // Block 0x21, offset 0x22 + {value: 0x0000, lo: 0x01}, + {value: 0x081f, lo: 0x8c, hi: 0x8c}, + // Block 0x22, offset 0x23 + {value: 0x0007, lo: 0x09}, + {value: 0x0823, lo: 0x83, hi: 0x83}, + {value: 0x082a, lo: 0x8d, hi: 0x8d}, + {value: 0x0831, lo: 0x92, hi: 0x92}, + {value: 0x0838, lo: 0x97, hi: 0x97}, + {value: 0x083f, lo: 0x9c, hi: 0x9c}, + {value: 0x0846, lo: 0xa9, hi: 0xa9}, + {value: 0x084d, lo: 0xb3, hi: 0xb3}, + {value: 0x0854, lo: 0xb5, hi: 0xb7}, + {value: 0x086c, lo: 0xb8, hi: 0xb9}, + // Block 0x23, offset 0x24 + {value: 0x0007, lo: 0x07}, + {value: 0x087d, lo: 0x81, hi: 0x81}, + {value: 0x0884, lo: 0x93, hi: 0x93}, + {value: 0x088b, lo: 0x9d, hi: 0x9d}, + {value: 0x0892, lo: 0xa2, hi: 0xa2}, + {value: 0x0899, lo: 0xa7, hi: 0xa7}, + {value: 0x08a0, lo: 0xac, hi: 0xac}, + {value: 0x08a7, lo: 0xb9, hi: 0xb9}, + // Block 0x24, offset 0x25 + {value: 0x0000, lo: 0x01}, + {value: 0x08ae, lo: 0xa6, hi: 0xa6}, + // Block 0x25, offset 0x26 + {value: 0x0000, lo: 0x01}, + {value: 0x08b5, lo: 0xbc, hi: 0xbc}, + // Block 0x26, offset 0x27 + {value: 0x0007, lo: 0x08}, + {value: 0x08b9, lo: 0x86, hi: 0x86}, + {value: 0x08c0, lo: 0x88, hi: 0x88}, + {value: 0x08c7, lo: 0x8a, hi: 0x8a}, + {value: 0x08ce, lo: 0x8c, hi: 0x8c}, + {value: 0x08d5, lo: 0x8e, hi: 0x8e}, + {value: 0x08dc, lo: 0x92, hi: 0x92}, + {value: 0x08e3, lo: 0xbb, hi: 0xbb}, + {value: 0x08ea, lo: 0xbd, hi: 0xbd}, + // Block 0x27, offset 0x28 + {value: 0x0007, lo: 0x02}, + {value: 0x08f1, lo: 0x80, hi: 0x81}, + {value: 0x08ff, lo: 0x83, hi: 0x83}, + // Block 0x28, offset 0x29 + {value: 0x0002, lo: 0x06}, + {value: 0x0906, lo: 0xac, hi: 0xad}, + {value: 0x090b, lo: 0xae, hi: 0xae}, + {value: 0x090d, lo: 0xb0, hi: 0xb2}, + {value: 0x0914, lo: 0xb3, hi: 0xba}, + {value: 0x0924, lo: 0xbc, hi: 0xbd}, + {value: 0x0929, lo: 0xbe, hi: 0xbf}, + // Block 0x29, offset 0x2a + {value: 0x0003, lo: 0x0a}, + {value: 0x0981, lo: 0x9b, hi: 0x9c}, + {value: 0x0986, lo: 0x9d, hi: 0x9e}, + {value: 0x0949, lo: 0x9f, hi: 0x9f}, + {value: 0x098c, lo: 0xa0, hi: 0xa0}, + {value: 0x098e, lo: 0xa1, hi: 0xa7}, + {value: 0x09a4, lo: 0xa8, hi: 0xaa}, + {value: 0x09ae, lo: 0xab, hi: 0xb8}, + {value: 0x09d9, lo: 0xb9, hi: 0xbb}, + {value: 0x09e1, lo: 0xbc, hi: 0xbe}, + {value: 0x055c, lo: 0xbf, hi: 0xbf}, + // Block 0x2a, offset 0x2b + {value: 0x0004, lo: 0x0d}, + {value: 0x09ea, lo: 0x80, hi: 0x88}, + {value: 0x0a10, lo: 0x89, hi: 0x89}, + {value: 0x0a16, lo: 0x8a, hi: 0x94}, + {value: 0x0a44, lo: 0x95, hi: 0x95}, + {value: 0x0a4a, lo: 0x96, hi: 0x96}, + {value: 0x0a50, lo: 0x97, hi: 0x97}, + {value: 0x0a56, lo: 0x98, hi: 0x9c}, + {value: 0x0a6c, lo: 0x9d, hi: 0x9d}, + {value: 0x0a72, lo: 0x9e, hi: 0xae}, + {value: 0x0ab8, lo: 0xaf, hi: 0xaf}, + {value: 0x0abe, lo: 0xb0, hi: 0xb8}, + {value: 0x0ae4, lo: 0xb9, hi: 0xb9}, + {value: 0x0aea, lo: 0xba, hi: 0xbf}, + // Block 0x2b, offset 0x2c + {value: 0x0000, lo: 0x0d}, + {value: 0x0001, lo: 0x80, hi: 0x8a}, + {value: 0x1436, lo: 0x91, hi: 0x91}, + {value: 0x143a, lo: 0x97, hi: 0x97}, + {value: 0x143e, lo: 0xa4, hi: 0xa4}, + {value: 0x1440, lo: 0xa5, hi: 0xa5}, + {value: 0x1443, lo: 0xa6, hi: 0xa6}, + {value: 0x0001, lo: 0xaf, hi: 0xaf}, + {value: 0x1447, lo: 0xb3, hi: 0xb3}, + {value: 0x144e, lo: 0xb4, hi: 0xb4}, + {value: 0x1458, lo: 0xb6, hi: 0xb6}, + {value: 0x145f, lo: 0xb7, hi: 0xb7}, + {value: 0x1469, lo: 0xbc, hi: 0xbc}, + {value: 0x146c, lo: 0xbe, hi: 0xbe}, + // Block 0x2c, offset 0x2d + {value: 0x0002, lo: 0x09}, + {value: 0x1470, lo: 0x87, hi: 0x87}, + {value: 0x1473, lo: 0x88, hi: 0x88}, + {value: 0x1476, lo: 0x89, hi: 0x89}, + {value: 0x1479, lo: 0x97, hi: 0x97}, + {value: 0x0001, lo: 0x9f, hi: 0x9f}, + {value: 0x1486, lo: 0xb0, hi: 0xb0}, + {value: 0x097c, lo: 0xb1, hi: 0xb1}, + {value: 0x1488, lo: 0xb4, hi: 0xbb}, + {value: 0x149a, lo: 0xbc, hi: 0xbf}, + // Block 0x2d, offset 0x2e + {value: 0x0006, lo: 0x03}, + {value: 0x1599, lo: 0x89, hi: 0x89}, + {value: 0x159f, lo: 0x9a, hi: 0x9b}, + {value: 0x15ab, lo: 0xae, hi: 0xae}, + // Block 0x2e, offset 0x2f + {value: 0x0006, lo: 0x01}, + {value: 0x15b1, lo: 0x8d, hi: 0x8f}, + // Block 0x2f, offset 0x30 + {value: 0x0006, lo: 0x09}, + {value: 0x15c3, lo: 0x84, hi: 0x84}, + {value: 0x15c9, lo: 0x89, hi: 0x89}, + {value: 0x15cf, lo: 0x8c, hi: 0x8c}, + {value: 0x15d5, lo: 0xa4, hi: 0xa4}, + {value: 0x15db, lo: 0xa6, hi: 0xa6}, + {value: 0x15e1, lo: 0xac, hi: 0xac}, + {value: 0x15e8, lo: 0xad, hi: 0xad}, + {value: 0x15f2, lo: 0xaf, hi: 0xaf}, + {value: 0x15f9, lo: 0xb0, hi: 0xb0}, + // Block 0x30, offset 0x31 + {value: 0x0006, lo: 0x0b}, + {value: 0x1603, lo: 0x81, hi: 0x81}, + {value: 0x1609, lo: 0x84, hi: 0x84}, + {value: 0x160f, lo: 0x87, hi: 0x87}, + {value: 0x1615, lo: 0x89, hi: 0x89}, + {value: 0x161b, lo: 0xa0, hi: 0xa0}, + {value: 0x161f, lo: 0xa2, hi: 0xa2}, + {value: 0x1625, lo: 0xad, hi: 0xae}, + {value: 0x162f, lo: 0xaf, hi: 0xaf}, + {value: 0x1633, lo: 0xb0, hi: 0xb1}, + {value: 0x163f, lo: 0xb4, hi: 0xb5}, + {value: 0x164b, lo: 0xb8, hi: 0xb9}, + // Block 0x31, offset 0x32 + {value: 0x0006, lo: 0x04}, + {value: 0x1657, lo: 0x80, hi: 0x81}, + {value: 0x1663, lo: 0x84, hi: 0x85}, + {value: 0x166f, lo: 0x88, hi: 0x89}, + {value: 0x167b, lo: 0xac, hi: 0xaf}, + // Block 0x32, offset 0x33 + {value: 0x0006, lo: 0x02}, + {value: 0x1693, lo: 0xa0, hi: 0xa3}, + {value: 0x16ab, lo: 0xaa, hi: 0xad}, + // Block 0x33, offset 0x34 + {value: 0x0004, lo: 0x01}, + {value: 0x16c3, lo: 0xa9, hi: 0xaa}, + // Block 0x34, offset 0x35 + {value: 0x0000, lo: 0x01}, + {value: 0x17fc, lo: 0x8c, hi: 0x8c}, + // Block 0x35, offset 0x36 + {value: 0x0004, lo: 0x02}, + {value: 0x1809, lo: 0xb4, hi: 0xb5}, + {value: 0x1810, lo: 0xb6, hi: 0xb6}, + // Block 0x36, offset 0x37 + {value: 0x0000, lo: 0x01}, + {value: 0x1814, lo: 0x9c, hi: 0x9c}, + // Block 0x37, offset 0x38 + {value: 0x10c6, lo: 0x01}, + {value: 0x0499, lo: 0xbc, hi: 0xbd}, + // Block 0x38, offset 0x39 + {value: 0x0000, lo: 0x01}, + {value: 0x181a, lo: 0xaf, hi: 0xaf}, + // Block 0x39, offset 0x3a + {value: 0x0000, lo: 0x01}, + {value: 0x181e, lo: 0x9f, hi: 0x9f}, + // Block 0x3a, offset 0x3b + {value: 0x0000, lo: 0x01}, + {value: 0x1822, lo: 0xb3, hi: 0xb3}, + // Block 0x3b, offset 0x3c + {value: 0x0004, lo: 0x01}, + {value: 0x1826, lo: 0x80, hi: 0xbf}, + // Block 0x3c, offset 0x3d + {value: 0x0004, lo: 0x01}, + {value: 0x1926, lo: 0x80, hi: 0xbf}, + // Block 0x3d, offset 0x3e + {value: 0x0004, lo: 0x01}, + {value: 0x1a26, lo: 0x80, hi: 0xbf}, + // Block 0x3e, offset 0x3f + {value: 0x0004, lo: 0x01}, + {value: 0x1b26, lo: 0x80, hi: 0x95}, + // Block 0x3f, offset 0x40 + {value: 0x0300, lo: 0x04}, + {value: 0x0001, lo: 0x80, hi: 0x80}, + {value: 0x1b7e, lo: 0xb6, hi: 0xb6}, + {value: 0x1882, lo: 0xb8, hi: 0xb9}, + {value: 0x1b86, lo: 0xba, hi: 0xba}, + // Block 0x40, offset 0x41 + {value: 0x0007, lo: 0x0e}, + {value: 0x1c39, lo: 0x94, hi: 0x94}, + {value: 0x1c40, lo: 0x9b, hi: 0x9b}, + {value: 0x1c45, lo: 0x9c, hi: 0x9c}, + {value: 0x1c4a, lo: 0x9e, hi: 0x9f}, + {value: 0x1c58, lo: 0xac, hi: 0xac}, + {value: 0x1c5f, lo: 0xae, hi: 0xae}, + {value: 0x1c66, lo: 0xb0, hi: 0xb0}, + {value: 0x1c6d, lo: 0xb2, hi: 0xb2}, + {value: 0x1c74, lo: 0xb4, hi: 0xb4}, + {value: 0x1c7b, lo: 0xb6, hi: 0xb6}, + {value: 0x1c82, lo: 0xb8, hi: 0xb8}, + {value: 0x1c89, lo: 0xba, hi: 0xba}, + {value: 0x1c90, lo: 0xbc, hi: 0xbc}, + {value: 0x1c97, lo: 0xbe, hi: 0xbe}, + // Block 0x41, offset 0x42 + {value: 0x0007, lo: 0x0d}, + {value: 0x1c9e, lo: 0x80, hi: 0x80}, + {value: 0x1ca5, lo: 0x82, hi: 0x82}, + {value: 0x1cac, lo: 0x85, hi: 0x85}, + {value: 0x1cb3, lo: 0x87, hi: 0x87}, + {value: 0x1cba, lo: 0x89, hi: 0x89}, + {value: 0x1cc1, lo: 0x90, hi: 0x91}, + {value: 0x1ccf, lo: 0x93, hi: 0x94}, + {value: 0x1cdd, lo: 0x96, hi: 0x97}, + {value: 0x1ceb, lo: 0x99, hi: 0x9a}, + {value: 0x1cf9, lo: 0x9c, hi: 0x9d}, + {value: 0x1d07, lo: 0xb4, hi: 0xb4}, + {value: 0x1d0e, lo: 0xb7, hi: 0xba}, + {value: 0x1d2a, lo: 0xbe, hi: 0xbf}, + // Block 0x42, offset 0x43 + {value: 0x0004, lo: 0x01}, + {value: 0x1d38, lo: 0xb1, hi: 0xbf}, + // Block 0x43, offset 0x44 + {value: 0x0004, lo: 0x01}, + {value: 0x1d74, lo: 0x80, hi: 0xbf}, + // Block 0x44, offset 0x45 + {value: 0x0004, lo: 0x07}, + {value: 0x1e74, lo: 0x80, hi: 0x8e}, + {value: 0x1826, lo: 0x92, hi: 0x92}, + {value: 0x183e, lo: 0x93, hi: 0x93}, + {value: 0x1eb0, lo: 0x94, hi: 0x99}, + {value: 0x1836, lo: 0x9a, hi: 0x9a}, + {value: 0x1ec8, lo: 0x9b, hi: 0x9e}, + {value: 0x1846, lo: 0x9f, hi: 0x9f}, + // Block 0x45, offset 0x46 + {value: 0x0004, lo: 0x0f}, + {value: 0x221c, lo: 0x80, hi: 0x80}, + {value: 0x2221, lo: 0x81, hi: 0x81}, + {value: 0x2226, lo: 0x82, hi: 0x82}, + {value: 0x222b, lo: 0x83, hi: 0x83}, + {value: 0x2230, lo: 0x84, hi: 0x84}, + {value: 0x2235, lo: 0x85, hi: 0x85}, + {value: 0x223a, lo: 0x86, hi: 0x86}, + {value: 0x223f, lo: 0x87, hi: 0x87}, + {value: 0x2244, lo: 0x88, hi: 0x88}, + {value: 0x2249, lo: 0x89, hi: 0x89}, + {value: 0x224f, lo: 0x8a, hi: 0x8a}, + {value: 0x2255, lo: 0x8b, hi: 0x8b}, + {value: 0x225b, lo: 0x8c, hi: 0x8c}, + {value: 0x225e, lo: 0x8d, hi: 0x8e}, + {value: 0x2265, lo: 0x8f, hi: 0xbe}, + // Block 0x46, offset 0x47 + {value: 0x0000, lo: 0x01}, + {value: 0x2a7d, lo: 0xb0, hi: 0xb0}, + // Block 0x47, offset 0x48 + {value: 0x0004, lo: 0x0a}, + {value: 0x2a81, lo: 0x80, hi: 0x81}, + {value: 0x1a9e, lo: 0x82, hi: 0x82}, + {value: 0x2a89, lo: 0x83, hi: 0x86}, + {value: 0x1b76, lo: 0x87, hi: 0x87}, + {value: 0x1b76, lo: 0x88, hi: 0x88}, + {value: 0x2a99, lo: 0x89, hi: 0x89}, + {value: 0x1abe, lo: 0x8a, hi: 0x8a}, + {value: 0x2a9d, lo: 0x8b, hi: 0xb3}, + {value: 0x1a16, lo: 0xb4, hi: 0xb4}, + {value: 0x2b41, lo: 0xb5, hi: 0xbf}, + // Block 0x48, offset 0x49 + {value: 0x0004, lo: 0x06}, + {value: 0x1b3a, lo: 0x80, hi: 0x80}, + {value: 0x2b6d, lo: 0x81, hi: 0x9b}, + {value: 0x2ac1, lo: 0x9c, hi: 0x9c}, + {value: 0x2bd9, lo: 0x9d, hi: 0xb0}, + {value: 0x1aa6, lo: 0xb1, hi: 0xb1}, + {value: 0x2c29, lo: 0xb2, hi: 0xbf}, + // Block 0x49, offset 0x4a + {value: 0x0004, lo: 0x0a}, + {value: 0x2c61, lo: 0x80, hi: 0x80}, + {value: 0x18ba, lo: 0x81, hi: 0x81}, + {value: 0x2c65, lo: 0x82, hi: 0x89}, + {value: 0x186e, lo: 0x8a, hi: 0x8a}, + {value: 0x2c85, lo: 0x8b, hi: 0xa0}, + {value: 0x2c21, lo: 0xa1, hi: 0xa1}, + {value: 0x2cdd, lo: 0xa2, hi: 0xa9}, + {value: 0x2be1, lo: 0xaa, hi: 0xaa}, + {value: 0x2cfd, lo: 0xab, hi: 0xbe}, + {value: 0x2ac1, lo: 0xbf, hi: 0xbf}, + // Block 0x4a, offset 0x4b + {value: 0x0004, lo: 0x0b}, + {value: 0x2d4d, lo: 0x80, hi: 0x83}, + {value: 0x1b72, lo: 0x84, hi: 0x84}, + {value: 0x2d5d, lo: 0x85, hi: 0x90}, + {value: 0x2173, lo: 0x91, hi: 0x91}, + {value: 0x2d8d, lo: 0x92, hi: 0x9a}, + {value: 0x2be9, lo: 0x9b, hi: 0x9b}, + {value: 0x2db1, lo: 0x9c, hi: 0xa8}, + {value: 0x1aba, lo: 0xa9, hi: 0xa9}, + {value: 0x2de5, lo: 0xaa, hi: 0xb6}, + {value: 0x19f6, lo: 0xb7, hi: 0xb7}, + {value: 0x2e19, lo: 0xb8, hi: 0xbf}, + // Block 0x4b, offset 0x4c + {value: 0x0004, lo: 0x10}, + {value: 0x2e39, lo: 0x80, hi: 0x87}, + {value: 0x1a62, lo: 0x88, hi: 0x88}, + {value: 0x2e59, lo: 0x89, hi: 0x89}, + {value: 0x1a6e, lo: 0x8a, hi: 0x8a}, + {value: 0x2e5d, lo: 0x8b, hi: 0x8d}, + {value: 0x2e69, lo: 0x90, hi: 0x90}, + {value: 0x2e6d, lo: 0x92, hi: 0x92}, + {value: 0x2e71, lo: 0x95, hi: 0x9d}, + {value: 0x1a12, lo: 0x9e, hi: 0x9e}, + {value: 0x2e95, lo: 0xa0, hi: 0xa0}, + {value: 0x2e99, lo: 0xa2, hi: 0xa2}, + {value: 0x2e9d, lo: 0xa5, hi: 0xa6}, + {value: 0x2ea5, lo: 0xaa, hi: 0xad}, + {value: 0x2eb5, lo: 0xb0, hi: 0xbb}, + {value: 0x18d6, lo: 0xbc, hi: 0xbc}, + {value: 0x2ee5, lo: 0xbd, hi: 0xbf}, + // Block 0x4c, offset 0x4d + {value: 0x0004, lo: 0x10}, + {value: 0x2ef1, lo: 0x80, hi: 0x8b}, + {value: 0x2187, lo: 0x8c, hi: 0x8c}, + {value: 0x2f21, lo: 0x8d, hi: 0x90}, + {value: 0x2197, lo: 0x91, hi: 0x91}, + {value: 0x2f31, lo: 0x92, hi: 0x96}, + {value: 0x2cb1, lo: 0x97, hi: 0x97}, + {value: 0x2f45, lo: 0x98, hi: 0x9d}, + {value: 0x2f59, lo: 0x9e, hi: 0xa6}, + {value: 0x2e9d, lo: 0xa7, hi: 0xa7}, + {value: 0x2f7d, lo: 0xa8, hi: 0xac}, + {value: 0x2f92, lo: 0xad, hi: 0xad}, + {value: 0x2f96, lo: 0xb0, hi: 0xb7}, + {value: 0x2ecd, lo: 0xb8, hi: 0xb8}, + {value: 0x2fb6, lo: 0xb9, hi: 0xbb}, + {value: 0x2e69, lo: 0xbc, hi: 0xbc}, + {value: 0x2fc2, lo: 0xbd, hi: 0xbf}, + // Block 0x4d, offset 0x4e + {value: 0x0005, lo: 0x06}, + {value: 0x3277, lo: 0x80, hi: 0x80}, + {value: 0x327e, lo: 0x81, hi: 0x81}, + {value: 0x3285, lo: 0x82, hi: 0x82}, + {value: 0x326d, lo: 0x83, hi: 0x83}, + {value: 0x328c, lo: 0x84, hi: 0x84}, + {value: 0x3293, lo: 0x85, hi: 0xbf}, + // Block 0x4e, offset 0x4f + {value: 0x0005, lo: 0x10}, + {value: 0x356a, lo: 0x80, hi: 0x8b}, + {value: 0x3514, lo: 0x8c, hi: 0x8c}, + {value: 0x35a6, lo: 0x8d, hi: 0x90}, + {value: 0x3533, lo: 0x91, hi: 0xa7}, + {value: 0x3514, lo: 0xa8, hi: 0xa8}, + {value: 0x35a6, lo: 0xa9, hi: 0xac}, + {value: 0x3597, lo: 0xad, hi: 0xaf}, + {value: 0x3514, lo: 0xb0, hi: 0xb0}, + {value: 0x350f, lo: 0xb1, hi: 0xb1}, + {value: 0x3519, lo: 0xb2, hi: 0xb2}, + {value: 0x333d, lo: 0xb3, hi: 0xb3}, + {value: 0x3306, lo: 0xb4, hi: 0xb6}, + {value: 0x3597, lo: 0xb7, hi: 0xb9}, + {value: 0x333d, lo: 0xba, hi: 0xbb}, + {value: 0x35ba, lo: 0xbc, hi: 0xbc}, + {value: 0x35ba, lo: 0xbd, hi: 0xbd}, + // Block 0x4f, offset 0x50 + {value: 0x0007, lo: 0x0d}, + {value: 0x35bf, lo: 0x90, hi: 0x91}, + {value: 0x35c6, lo: 0x92, hi: 0x98}, + {value: 0x35f0, lo: 0x99, hi: 0x9f}, + {value: 0x361a, lo: 0xa0, hi: 0xa2}, + {value: 0x3628, lo: 0xa3, hi: 0xa4}, + {value: 0x362f, lo: 0xa5, hi: 0xa7}, + {value: 0x363d, lo: 0xa8, hi: 0xaa}, + {value: 0x364b, lo: 0xab, hi: 0xac}, + {value: 0x3652, lo: 0xad, hi: 0xaf}, + {value: 0x3660, lo: 0xb0, hi: 0xb1}, + {value: 0x3667, lo: 0xb2, hi: 0xb6}, + {value: 0x3683, lo: 0xb7, hi: 0xbc}, + {value: 0x36a6, lo: 0xbd, hi: 0xbf}, + // Block 0x50, offset 0x51 + {value: 0x0007, lo: 0x0d}, + {value: 0x36bb, lo: 0x80, hi: 0x83}, + {value: 0x36d0, lo: 0x84, hi: 0x85}, + {value: 0x36d7, lo: 0x86, hi: 0x87}, + {value: 0x36de, lo: 0x88, hi: 0x8f}, + {value: 0x3716, lo: 0x92, hi: 0x97}, + {value: 0x3739, lo: 0x98, hi: 0x9c}, + {value: 0x3755, lo: 0x9d, hi: 0xb3}, + {value: 0x36ad, lo: 0xb4, hi: 0xb4}, + {value: 0x36bb, lo: 0xb5, hi: 0xb5}, + {value: 0x37f6, lo: 0xb6, hi: 0xbb}, + {value: 0x3812, lo: 0xbc, hi: 0xbc}, + {value: 0x3804, lo: 0xbd, hi: 0xbd}, + {value: 0x3820, lo: 0xbe, hi: 0xbf}, + // Block 0x51, offset 0x52 + {value: 0x0009, lo: 0x0e}, + {value: 0x382e, lo: 0x80, hi: 0x80}, + {value: 0x3835, lo: 0x81, hi: 0x81}, + {value: 0x383c, lo: 0x82, hi: 0x82}, + {value: 0x3819, lo: 0x83, hi: 0x83}, + {value: 0x367c, lo: 0x84, hi: 0x84}, + {value: 0x3636, lo: 0x85, hi: 0x85}, + {value: 0x3843, lo: 0x86, hi: 0x86}, + {value: 0x384a, lo: 0x87, hi: 0x87}, + {value: 0x3851, lo: 0xb0, hi: 0xb0}, + {value: 0x3858, lo: 0xb1, hi: 0xb1}, + {value: 0x385f, lo: 0xb2, hi: 0xb9}, + {value: 0x38a5, lo: 0xba, hi: 0xba}, + {value: 0x38c7, lo: 0xbb, hi: 0xbb}, + {value: 0x38d7, lo: 0xbc, hi: 0xbc}, + // Block 0x52, offset 0x53 + {value: 0x0004, lo: 0x10}, + {value: 0x38e0, lo: 0x90, hi: 0x90}, + {value: 0x38e2, lo: 0x91, hi: 0x93}, + {value: 0x04e1, lo: 0x94, hi: 0x94}, + {value: 0x38ec, lo: 0x95, hi: 0x95}, + {value: 0x38ee, lo: 0x96, hi: 0x96}, + {value: 0x38f0, lo: 0x97, hi: 0x98}, + {value: 0x1443, lo: 0x99, hi: 0x99}, + {value: 0x1440, lo: 0xb0, hi: 0xb0}, + {value: 0x38f8, lo: 0xb1, hi: 0xb3}, + {value: 0x3900, lo: 0xb4, hi: 0xb4}, + {value: 0x149c, lo: 0xb5, hi: 0xb5}, + {value: 0x149e, lo: 0xb6, hi: 0xb6}, + {value: 0x3902, lo: 0xb7, hi: 0xb7}, + {value: 0x3904, lo: 0xb8, hi: 0xb8}, + {value: 0x3906, lo: 0xb9, hi: 0xbe}, + {value: 0x16c3, lo: 0xbf, hi: 0xbf}, + // Block 0x53, offset 0x54 + {value: 0x0004, lo: 0x04}, + {value: 0x22a5, lo: 0x80, hi: 0x9c}, + {value: 0x3a38, lo: 0x9d, hi: 0x9f}, + {value: 0x1e04, lo: 0xa0, hi: 0xa0}, + {value: 0x1d38, lo: 0xa1, hi: 0xbe}, + // Block 0x54, offset 0x55 + {value: 0x0004, lo: 0x0c}, + {value: 0x1db0, lo: 0x82, hi: 0x87}, + {value: 0x1dc8, lo: 0x8a, hi: 0x8f}, + {value: 0x1de0, lo: 0x92, hi: 0x97}, + {value: 0x1df8, lo: 0x9a, hi: 0x9c}, + {value: 0x3a44, lo: 0xa0, hi: 0xa0}, + {value: 0x3a47, lo: 0xa1, hi: 0xa1}, + {value: 0x3a4a, lo: 0xa2, hi: 0xa2}, + {value: 0x0009, lo: 0xa3, hi: 0xa3}, + {value: 0x3a4d, lo: 0xa4, hi: 0xa4}, + {value: 0x3a50, lo: 0xa5, hi: 0xa5}, + {value: 0x3a53, lo: 0xa6, hi: 0xa6}, + {value: 0x3a57, lo: 0xa8, hi: 0xae}, + // Block 0x55, offset 0x56 + {value: 0x0009, lo: 0x03}, + {value: 0x3a73, lo: 0x9a, hi: 0x9a}, + {value: 0x3a7c, lo: 0x9c, hi: 0x9c}, + {value: 0x3a85, lo: 0xab, hi: 0xab}, + // Block 0x56, offset 0x57 + {value: 0x000d, lo: 0x03}, + {value: 0x3a8e, lo: 0x9e, hi: 0x9e}, + {value: 0x3a97, lo: 0x9f, hi: 0x9f}, + {value: 0x3aa0, lo: 0xa0, hi: 0xa4}, + // Block 0x57, offset 0x58 + {value: 0x0009, lo: 0x03}, + {value: 0x3ae1, lo: 0xbb, hi: 0xbd}, + {value: 0x3b00, lo: 0xbe, hi: 0xbe}, + {value: 0x3b0d, lo: 0xbf, hi: 0xbf}, + // Block 0x58, offset 0x59 + {value: 0x0000, lo: 0x01}, + {value: 0x3b1a, lo: 0x80, hi: 0x80}, + // Block 0x59, offset 0x5a + {value: 0x0003, lo: 0x0e}, + {value: 0x14c8, lo: 0x80, hi: 0x80}, + {value: 0x092b, lo: 0x81, hi: 0x81}, + {value: 0x17f6, lo: 0x82, hi: 0x82}, + {value: 0x092d, lo: 0x83, hi: 0x83}, + {value: 0x092f, lo: 0x84, hi: 0x84}, + {value: 0x155f, lo: 0x85, hi: 0x85}, + {value: 0x0931, lo: 0x86, hi: 0x86}, + {value: 0x1570, lo: 0x87, hi: 0x87}, + {value: 0x17f8, lo: 0x88, hi: 0x88}, + {value: 0x14d4, lo: 0x89, hi: 0x89}, + {value: 0x3c2c, lo: 0x8a, hi: 0x8a}, + {value: 0x293d, lo: 0x8b, hi: 0x8b}, + {value: 0x3c2f, lo: 0x8c, hi: 0x8e}, + {value: 0x3c39, lo: 0x8f, hi: 0x8f}, + // Block 0x5a, offset 0x5b + {value: 0x0000, lo: 0x01}, + {value: 0x3c3c, lo: 0x90, hi: 0x90}, + // Block 0x5b, offset 0x5c + {value: 0x000a, lo: 0x03}, + {value: 0x3cc1, lo: 0x80, hi: 0x88}, + {value: 0x3d1b, lo: 0x90, hi: 0x90}, + {value: 0x3d1f, lo: 0x91, hi: 0x91}, + // Block 0x5c, offset 0x5d + {value: 0x0004, lo: 0x0d}, + {value: 0x4463, lo: 0x80, hi: 0x81}, + {value: 0x446c, lo: 0x82, hi: 0x89}, + {value: 0x30a2, lo: 0x8a, hi: 0x8a}, + {value: 0x448d, lo: 0x8b, hi: 0x90}, + {value: 0x44a6, lo: 0x91, hi: 0x92}, + {value: 0x44af, lo: 0x93, hi: 0x93}, + {value: 0x44b4, lo: 0x94, hi: 0x94}, + {value: 0x1b42, lo: 0x95, hi: 0x95}, + {value: 0x44b9, lo: 0x96, hi: 0x96}, + {value: 0x1b52, lo: 0x97, hi: 0x97}, + {value: 0x44bd, lo: 0x98, hi: 0x9b}, + {value: 0x1b66, lo: 0x9c, hi: 0x9c}, + {value: 0x44cd, lo: 0x9d, hi: 0x9d}, } // nfkcDecompLookup: 960 bytes @@ -4367,51 +4608,51 @@ var nfkcDecompLookup = [960]uint8{ // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x0c2: 0x03, 0x0c3: 0x04, 0x0c4: 0x05, 0x0c5: 0x06, 0x0c6: 0x07, 0x0c7: 0x08, - 0x0c8: 0x09, 0x0ca: 0x0a, 0x0cb: 0x0b, 0x0cd: 0x0c, 0x0ce: 0x0d, 0x0cf: 0x0e, - 0x0d0: 0x0f, 0x0d1: 0x10, 0x0d3: 0x11, 0x0d6: 0x12, - 0x0d8: 0x13, 0x0d9: 0x14, 0x0db: 0x15, + 0x0c2: 0x42, 0x0c3: 0x43, 0x0c4: 0x44, 0x0c5: 0x45, 0x0c6: 0x46, 0x0c7: 0x03, + 0x0c8: 0x47, 0x0ca: 0x48, 0x0cb: 0x49, 0x0cd: 0x4a, 0x0ce: 0x4b, 0x0cf: 0x4c, + 0x0d0: 0x4d, 0x0d1: 0x4e, 0x0d3: 0x4f, 0x0d6: 0x50, + 0x0d8: 0x51, 0x0d9: 0x52, 0x0db: 0x53, 0x0e0: 0x04, 0x0e1: 0x05, 0x0e2: 0x06, 0x0e3: 0x07, 0x0ea: 0x08, 0x0ef: 0x09, 0x0f0: 0x0e, // Block 0x4, offset 0x100 - 0x124: 0x16, 0x125: 0x17, 0x127: 0x18, - 0x128: 0x19, 0x129: 0x1a, 0x12d: 0x1b, 0x12e: 0x1c, 0x12f: 0x1d, - 0x131: 0x1e, 0x133: 0x1f, 0x135: 0x20, 0x137: 0x21, - 0x138: 0x22, 0x13a: 0x23, 0x13b: 0x24, 0x13c: 0x25, 0x13d: 0x26, 0x13e: 0x27, + 0x124: 0x54, 0x125: 0x55, 0x127: 0x56, + 0x128: 0x57, 0x129: 0x58, 0x12d: 0x59, 0x12e: 0x5a, 0x12f: 0x5b, + 0x131: 0x5c, 0x133: 0x5d, 0x135: 0x5e, 0x137: 0x5f, + 0x138: 0x60, 0x13a: 0x61, 0x13b: 0x62, 0x13c: 0x63, 0x13d: 0x64, 0x13e: 0x65, // Block 0x5, offset 0x140 - 0x140: 0x28, 0x143: 0x29, - 0x16c: 0x2a, 0x16d: 0x2b, - 0x174: 0x2c, 0x175: 0x2d, 0x176: 0x2e, - 0x178: 0x2f, 0x179: 0x30, 0x17a: 0x31, 0x17b: 0x32, 0x17c: 0x33, 0x17d: 0x34, 0x17e: 0x35, 0x17f: 0x36, + 0x140: 0x66, 0x143: 0x67, + 0x16c: 0x68, 0x16d: 0x69, + 0x174: 0x6a, 0x175: 0x04, 0x176: 0x6b, + 0x178: 0x6c, 0x179: 0x05, 0x17a: 0x06, 0x17b: 0x07, 0x17c: 0x08, 0x17d: 0x09, 0x17e: 0x0a, 0x17f: 0x0b, // Block 0x6, offset 0x180 - 0x180: 0x37, 0x181: 0x38, 0x182: 0x39, 0x184: 0x3a, 0x185: 0x3b, 0x186: 0x3c, 0x187: 0x3d, - 0x188: 0x3e, 0x189: 0x3f, 0x18a: 0x40, 0x18b: 0x41, 0x18c: 0x42, - 0x191: 0x43, 0x192: 0x44, 0x193: 0x45, - 0x1a8: 0x46, 0x1a9: 0x47, 0x1ab: 0x48, - 0x1b1: 0x49, 0x1b5: 0x4a, - 0x1ba: 0x4b, 0x1bb: 0x4c, 0x1bc: 0x4d, 0x1bd: 0x4e, 0x1be: 0x4f, 0x1bf: 0x50, + 0x180: 0x6d, 0x181: 0x6e, 0x182: 0x0c, 0x184: 0x0d, 0x185: 0x0e, 0x186: 0x6f, 0x187: 0x70, + 0x188: 0x71, 0x189: 0x72, 0x18a: 0x73, 0x18b: 0x74, 0x18c: 0x75, + 0x191: 0x0f, 0x192: 0x10, 0x193: 0x11, + 0x1a8: 0x76, 0x1a9: 0x77, 0x1ab: 0x78, + 0x1b1: 0x79, 0x1b5: 0x7a, + 0x1ba: 0x7b, 0x1bb: 0x7c, 0x1bc: 0x7d, 0x1bd: 0x7e, 0x1be: 0x7f, 0x1bf: 0x80, // Block 0x7, offset 0x1c0 - 0x1c0: 0x51, 0x1c1: 0x52, 0x1c2: 0x53, 0x1c3: 0x54, 0x1c4: 0x55, 0x1c5: 0x56, 0x1c6: 0x57, - 0x1c8: 0x58, 0x1c9: 0x59, 0x1ca: 0x5a, 0x1cb: 0x5b, 0x1cc: 0x5c, 0x1cd: 0x5d, 0x1ce: 0x5e, 0x1cf: 0x5f, + 0x1c0: 0x81, 0x1c1: 0x12, 0x1c2: 0x82, 0x1c3: 0x83, 0x1c4: 0x84, 0x1c5: 0x85, 0x1c6: 0x86, + 0x1c8: 0x13, 0x1c9: 0x14, 0x1ca: 0x15, 0x1cb: 0x87, 0x1cc: 0x16, 0x1cd: 0x17, 0x1ce: 0x18, 0x1cf: 0x19, // Block 0x8, offset 0x200 - 0x21d: 0x60, + 0x21d: 0x88, // Block 0x9, offset 0x240 - 0x264: 0x61, 0x265: 0x62, 0x266: 0x63, 0x267: 0x64, - 0x268: 0x65, 0x269: 0x66, 0x26a: 0x67, 0x26b: 0x68, 0x26c: 0x69, 0x26d: 0x6a, 0x26e: 0x6b, 0x26f: 0x6c, - 0x270: 0x6d, 0x271: 0x6e, 0x272: 0x6f, 0x273: 0x70, 0x274: 0x71, 0x275: 0x72, 0x276: 0x73, 0x277: 0x74, - 0x278: 0x75, 0x279: 0x76, 0x27a: 0x77, 0x27b: 0x78, 0x27c: 0x79, 0x27d: 0x7a, 0x27e: 0x7b, 0x27f: 0x7c, + 0x264: 0x89, 0x265: 0x8a, 0x266: 0x8b, 0x267: 0x8c, + 0x268: 0x8d, 0x269: 0x8e, 0x26a: 0x1a, 0x26b: 0x1b, 0x26c: 0x1c, 0x26d: 0x1d, 0x26e: 0x1e, 0x26f: 0x1f, + 0x270: 0x8f, 0x271: 0x20, 0x272: 0x21, 0x273: 0x22, 0x274: 0x90, 0x275: 0x91, 0x276: 0x92, 0x277: 0x93, + 0x278: 0x94, 0x279: 0x23, 0x27a: 0x24, 0x27b: 0x25, 0x27c: 0x26, 0x27d: 0x27, 0x27e: 0x95, 0x27f: 0x96, // Block 0xa, offset 0x280 - 0x282: 0x7d, + 0x282: 0x97, // Block 0xb, offset 0x2c0 - 0x2c5: 0x7e, 0x2c6: 0x7f, 0x2c7: 0x80, - 0x2d0: 0x81, 0x2d1: 0x82, 0x2d2: 0x83, 0x2d3: 0x84, 0x2d4: 0x85, 0x2d5: 0x86, 0x2d6: 0x87, 0x2d7: 0x88, - 0x2d8: 0x89, 0x2d9: 0x8a, 0x2da: 0x8b, 0x2db: 0x8c, 0x2dc: 0x8d, 0x2dd: 0x8e, 0x2de: 0x8f, 0x2df: 0x90, + 0x2c5: 0x98, 0x2c6: 0x99, 0x2c7: 0x9a, + 0x2d0: 0x28, 0x2d1: 0x29, 0x2d2: 0x2a, 0x2d3: 0x2b, 0x2d4: 0x2c, 0x2d5: 0x2d, 0x2d6: 0x2e, 0x2d7: 0x2f, + 0x2d8: 0x30, 0x2d9: 0x31, 0x2da: 0x32, 0x2db: 0x33, 0x2dc: 0x34, 0x2dd: 0x35, 0x2de: 0x36, 0x2df: 0x37, // Block 0xc, offset 0x300 - 0x304: 0x91, 0x305: 0x92, 0x306: 0x93, - 0x308: 0x94, 0x309: 0x95, + 0x304: 0x38, 0x305: 0x9b, 0x306: 0x9c, + 0x308: 0x39, 0x309: 0x9d, // Block 0xd, offset 0x340 - 0x360: 0x96, 0x361: 0x97, 0x362: 0x98, 0x363: 0x99, 0x364: 0x9a, 0x365: 0x9b, 0x366: 0x9c, 0x367: 0x9d, + 0x360: 0x3a, 0x361: 0x3b, 0x362: 0x3c, 0x363: 0x3d, 0x364: 0x3e, 0x365: 0x3f, 0x366: 0x40, 0x367: 0x41, 0x368: 0x9e, // Block 0xe, offset 0x380 0x391: 0x0a, @@ -4419,7 +4660,7 @@ var nfkcDecompLookup = [960]uint8{ 0x3af: 0x0d, } -var nfkcDecompTrie = trie{nfkcDecompLookup[:], nfkcDecompValues[:]} +var nfkcDecompTrie = trie{nfkcDecompLookup[:], nfkcDecompValues[:], nfkcDecompSparseValues[:], nfkcDecompSparseOffset[:], 66} // recompMap: 7448 bytes (entries only) var recompMap = map[uint32]uint32{ @@ -5356,9 +5597,9 @@ var recompMap = map[uint32]uint32{ 0x10A510BA: 0x110AB, } -// charInfoValues: 10944 entries, 21888 bytes +// charInfoValues: 1024 entries, 2048 bytes // Block 2 is the null block. -var charInfoValues = [10944]uint16{ +var charInfoValues = [1024]uint16{ // Block 0x0, offset 0x0 0x003c: 0x8800, 0x003d: 0x8800, 0x003e: 0x8800, // Block 0x1, offset 0x40 @@ -5374,1121 +5615,1071 @@ var charInfoValues = [10944]uint16{ 0x0076: 0x8800, 0x0077: 0x8800, 0x0078: 0x8800, 0x0079: 0x8800, 0x007a: 0x8800, // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x00e0: 0x3000, - 0x00e8: 0x3800, - 0x00ea: 0x3000, 0x00ef: 0x3000, - 0x00f2: 0x3000, 0x00f3: 0x3000, 0x00f4: 0x3000, 0x00f5: 0x3000, - 0x00f8: 0x3000, 0x00f9: 0x3000, 0x00fa: 0x3000, - 0x00fc: 0x3000, 0x00fd: 0x3000, 0x00fe: 0x3000, + 0x00c0: 0x1100, 0x00c1: 0x1100, 0x00c2: 0x9900, 0x00c3: 0x1100, 0x00c4: 0x9900, 0x00c5: 0x9900, + 0x00c6: 0x8800, 0x00c7: 0x9900, 0x00c8: 0x1100, 0x00c9: 0x1100, 0x00ca: 0x9900, 0x00cb: 0x1100, + 0x00cc: 0x1100, 0x00cd: 0x1100, 0x00ce: 0x1100, 0x00cf: 0x9900, 0x00d1: 0x1100, + 0x00d2: 0x1100, 0x00d3: 0x1100, 0x00d4: 0x9900, 0x00d5: 0x9900, 0x00d6: 0x9900, + 0x00d8: 0x8800, 0x00d9: 0x1100, 0x00da: 0x1100, 0x00db: 0x1100, 0x00dc: 0x9900, 0x00dd: 0x1100, + 0x00e0: 0x1100, 0x00e1: 0x1100, 0x00e2: 0x9900, 0x00e3: 0x1100, + 0x00e4: 0x9900, 0x00e5: 0x9900, 0x00e6: 0x8800, 0x00e7: 0x9900, 0x00e8: 0x1100, 0x00e9: 0x1100, + 0x00ea: 0x9900, 0x00eb: 0x1100, 0x00ec: 0x1100, 0x00ed: 0x1100, 0x00ee: 0x1100, 0x00ef: 0x9900, + 0x00f1: 0x1100, 0x00f2: 0x1100, 0x00f3: 0x1100, 0x00f4: 0x9900, 0x00f5: 0x9900, + 0x00f6: 0x9900, 0x00f8: 0x8800, 0x00f9: 0x1100, 0x00fa: 0x1100, 0x00fb: 0x1100, + 0x00fc: 0x9900, 0x00fd: 0x1100, 0x00ff: 0x1100, // Block 0x4, offset 0x100 - 0x0100: 0x1100, 0x0101: 0x1100, 0x0102: 0x9900, 0x0103: 0x1100, 0x0104: 0x9900, 0x0105: 0x9900, - 0x0106: 0x8800, 0x0107: 0x9900, 0x0108: 0x1100, 0x0109: 0x1100, 0x010a: 0x9900, 0x010b: 0x1100, - 0x010c: 0x1100, 0x010d: 0x1100, 0x010e: 0x1100, 0x010f: 0x9900, 0x0111: 0x1100, - 0x0112: 0x1100, 0x0113: 0x1100, 0x0114: 0x9900, 0x0115: 0x9900, 0x0116: 0x9900, - 0x0118: 0x8800, 0x0119: 0x1100, 0x011a: 0x1100, 0x011b: 0x1100, 0x011c: 0x9900, 0x011d: 0x1100, - 0x0120: 0x1100, 0x0121: 0x1100, 0x0122: 0x9900, 0x0123: 0x1100, - 0x0124: 0x9900, 0x0125: 0x9900, 0x0126: 0x8800, 0x0127: 0x9900, 0x0128: 0x1100, 0x0129: 0x1100, - 0x012a: 0x9900, 0x012b: 0x1100, 0x012c: 0x1100, 0x012d: 0x1100, 0x012e: 0x1100, 0x012f: 0x9900, - 0x0131: 0x1100, 0x0132: 0x1100, 0x0133: 0x1100, 0x0134: 0x9900, 0x0135: 0x9900, - 0x0136: 0x9900, 0x0138: 0x8800, 0x0139: 0x1100, 0x013a: 0x1100, 0x013b: 0x1100, - 0x013c: 0x9900, 0x013d: 0x1100, 0x013f: 0x1100, + 0x0100: 0x66e6, 0x0101: 0x66e6, 0x0102: 0x66e6, 0x0103: 0x66e6, 0x0104: 0x66e6, 0x0105: 0x00e6, + 0x0106: 0x66e6, 0x0107: 0x66e6, 0x0108: 0x66e6, 0x0109: 0x66e6, 0x010a: 0x66e6, 0x010b: 0x66e6, + 0x010c: 0x66e6, 0x010d: 0x00e6, 0x010e: 0x00e6, 0x010f: 0x66e6, 0x0110: 0x00e6, 0x0111: 0x66e6, + 0x0112: 0x00e6, 0x0113: 0x66e6, 0x0114: 0x66e6, 0x0115: 0x00e8, 0x0116: 0x00dc, 0x0117: 0x00dc, + 0x0118: 0x00dc, 0x0119: 0x00dc, 0x011a: 0x00e8, 0x011b: 0x66d8, 0x011c: 0x00dc, 0x011d: 0x00dc, + 0x011e: 0x00dc, 0x011f: 0x00dc, 0x0120: 0x00dc, 0x0121: 0x00ca, 0x0122: 0x00ca, 0x0123: 0x66dc, + 0x0124: 0x66dc, 0x0125: 0x66dc, 0x0126: 0x66dc, 0x0127: 0x66ca, 0x0128: 0x66ca, 0x0129: 0x00dc, + 0x012a: 0x00dc, 0x012b: 0x00dc, 0x012c: 0x00dc, 0x012d: 0x66dc, 0x012e: 0x66dc, 0x012f: 0x00dc, + 0x0130: 0x66dc, 0x0131: 0x66dc, 0x0132: 0x00dc, 0x0133: 0x00dc, 0x0134: 0x0001, 0x0135: 0x0001, + 0x0136: 0x0001, 0x0137: 0x0001, 0x0138: 0x6601, 0x0139: 0x00dc, 0x013a: 0x00dc, 0x013b: 0x00dc, + 0x013c: 0x00dc, 0x013d: 0x00e6, 0x013e: 0x00e6, 0x013f: 0x00e6, // Block 0x5, offset 0x140 - 0x0140: 0x1100, 0x0141: 0x1100, 0x0142: 0x9900, 0x0143: 0x9900, 0x0144: 0x1100, 0x0145: 0x1100, - 0x0146: 0x1100, 0x0147: 0x1100, 0x0148: 0x1100, 0x0149: 0x1100, 0x014a: 0x1100, 0x014b: 0x1100, - 0x014c: 0x1100, 0x014d: 0x1100, 0x014e: 0x1100, 0x014f: 0x1100, - 0x0152: 0x9900, 0x0153: 0x9900, 0x0154: 0x1100, 0x0155: 0x1100, 0x0156: 0x1100, 0x0157: 0x1100, - 0x0158: 0x1100, 0x0159: 0x1100, 0x015a: 0x1100, 0x015b: 0x1100, 0x015c: 0x1100, 0x015d: 0x1100, - 0x015e: 0x1100, 0x015f: 0x1100, 0x0160: 0x1100, 0x0161: 0x1100, 0x0162: 0x1100, 0x0163: 0x1100, - 0x0164: 0x1100, 0x0165: 0x1100, 0x0168: 0x1100, 0x0169: 0x1100, - 0x016a: 0x1100, 0x016b: 0x1100, 0x016c: 0x1100, 0x016d: 0x1100, 0x016e: 0x1100, 0x016f: 0x1100, - 0x0170: 0x1100, 0x0172: 0x3000, 0x0173: 0x3000, 0x0174: 0x1100, 0x0175: 0x1100, - 0x0176: 0x1100, 0x0177: 0x1100, 0x0179: 0x1100, 0x017a: 0x1100, 0x017b: 0x1100, - 0x017c: 0x1100, 0x017d: 0x1100, 0x017e: 0x1100, 0x017f: 0x3000, + 0x0140: 0x33e6, 0x0141: 0x33e6, 0x0142: 0x66e6, 0x0143: 0x33e6, 0x0144: 0x33e6, 0x0145: 0x66f0, + 0x0146: 0x00e6, 0x0147: 0x00dc, 0x0148: 0x00dc, 0x0149: 0x00dc, 0x014a: 0x00e6, 0x014b: 0x00e6, + 0x014c: 0x00e6, 0x014d: 0x00dc, 0x014e: 0x00dc, 0x0150: 0x00e6, 0x0151: 0x00e6, + 0x0152: 0x00e6, 0x0153: 0x00dc, 0x0154: 0x00dc, 0x0155: 0x00dc, 0x0156: 0x00dc, 0x0157: 0x00e6, + 0x0158: 0x00e8, 0x0159: 0x00dc, 0x015a: 0x00dc, 0x015b: 0x00e6, 0x015c: 0x00e9, 0x015d: 0x00ea, + 0x015e: 0x00ea, 0x015f: 0x00e9, 0x0160: 0x00ea, 0x0161: 0x00ea, 0x0162: 0x00e9, 0x0163: 0x00e6, + 0x0164: 0x00e6, 0x0165: 0x00e6, 0x0166: 0x00e6, 0x0167: 0x00e6, 0x0168: 0x00e6, 0x0169: 0x00e6, + 0x016a: 0x00e6, 0x016b: 0x00e6, 0x016c: 0x00e6, 0x016d: 0x00e6, 0x016e: 0x00e6, 0x016f: 0x00e6, + 0x0174: 0x3300, + 0x017a: 0x3000, + 0x017e: 0x3300, // Block 0x6, offset 0x180 - 0x0180: 0x3000, 0x0183: 0x1100, 0x0184: 0x1100, 0x0185: 0x1100, - 0x0186: 0x1100, 0x0187: 0x1100, 0x0188: 0x1100, 0x0189: 0x3000, - 0x018c: 0x9900, 0x018d: 0x9900, 0x018e: 0x1100, 0x018f: 0x1100, 0x0190: 0x1100, 0x0191: 0x1100, - 0x0194: 0x1100, 0x0195: 0x1100, 0x0196: 0x1100, 0x0197: 0x1100, - 0x0198: 0x1100, 0x0199: 0x1100, 0x019a: 0x9900, 0x019b: 0x9900, 0x019c: 0x1100, 0x019d: 0x1100, - 0x019e: 0x1100, 0x019f: 0x1100, 0x01a0: 0x9900, 0x01a1: 0x9900, 0x01a2: 0x1100, 0x01a3: 0x1100, - 0x01a4: 0x1100, 0x01a5: 0x1100, 0x01a8: 0x9900, 0x01a9: 0x9900, - 0x01aa: 0x9900, 0x01ab: 0x9900, 0x01ac: 0x1100, 0x01ad: 0x1100, 0x01ae: 0x1100, 0x01af: 0x1100, - 0x01b0: 0x1100, 0x01b1: 0x1100, 0x01b2: 0x1100, 0x01b3: 0x1100, 0x01b4: 0x1100, 0x01b5: 0x1100, - 0x01b6: 0x1100, 0x01b7: 0x1100, 0x01b8: 0x1100, 0x01b9: 0x1100, 0x01ba: 0x1100, 0x01bb: 0x1100, - 0x01bc: 0x1100, 0x01bd: 0x1100, 0x01be: 0x1100, 0x01bf: 0x3800, + 0x0184: 0x3000, 0x0185: 0x3100, + 0x0186: 0x1100, 0x0187: 0x3300, 0x0188: 0x1100, 0x0189: 0x1100, 0x018a: 0x1100, + 0x018c: 0x1100, 0x018e: 0x1100, 0x018f: 0x1100, 0x0190: 0x1100, 0x0191: 0x8800, + 0x0195: 0x8800, 0x0197: 0x8800, + 0x0199: 0x8800, + 0x019f: 0x8800, 0x01a1: 0x8800, + 0x01a5: 0x8800, 0x01a9: 0x8800, + 0x01aa: 0x1100, 0x01ab: 0x1100, 0x01ac: 0x9900, 0x01ad: 0x1100, 0x01ae: 0x9900, 0x01af: 0x1100, + 0x01b0: 0x1100, 0x01b1: 0x8800, 0x01b5: 0x8800, + 0x01b7: 0x8800, 0x01b9: 0x8800, + 0x01bf: 0x8800, // Block 0x7, offset 0x1c0 - 0x01e0: 0x9900, 0x01e1: 0x9900, - 0x01ef: 0x9900, - 0x01f0: 0x9900, - 0x01f7: 0x8800, + 0x01c0: 0x1100, 0x01c1: 0x1100, 0x01c3: 0x1100, + 0x01c6: 0x8800, 0x01c7: 0x1100, + 0x01cc: 0x1100, 0x01cd: 0x1100, 0x01ce: 0x1100, 0x01d0: 0x8800, + 0x01d3: 0x8800, 0x01d5: 0x8800, 0x01d6: 0x8800, 0x01d7: 0x8800, + 0x01d8: 0x8800, 0x01d9: 0x1100, 0x01da: 0x8800, + 0x01de: 0x8800, 0x01e3: 0x8800, + 0x01e7: 0x8800, + 0x01eb: 0x8800, 0x01ed: 0x8800, + 0x01f0: 0x8800, 0x01f3: 0x8800, 0x01f5: 0x8800, + 0x01f6: 0x8800, 0x01f7: 0x8800, 0x01f8: 0x8800, 0x01f9: 0x1100, 0x01fa: 0x8800, + 0x01fe: 0x8800, // Block 0x8, offset 0x200 - 0x0204: 0x3000, 0x0205: 0x3000, - 0x0206: 0x3000, 0x0207: 0x3000, 0x0208: 0x3000, 0x0209: 0x3000, 0x020a: 0x3000, 0x020b: 0x3000, - 0x020c: 0x3000, 0x020d: 0x1100, 0x020e: 0x1100, 0x020f: 0x1100, 0x0210: 0x1100, 0x0211: 0x1100, - 0x0212: 0x1100, 0x0213: 0x1100, 0x0214: 0x1100, 0x0215: 0x1100, 0x0216: 0x1100, 0x0217: 0x1100, - 0x0218: 0x1100, 0x0219: 0x1100, 0x021a: 0x1100, 0x021b: 0x1100, 0x021c: 0x1100, - 0x021e: 0x1100, 0x021f: 0x1100, 0x0220: 0x1100, 0x0221: 0x1100, 0x0222: 0x1100, 0x0223: 0x1100, - 0x0226: 0x1100, 0x0227: 0x1100, 0x0228: 0x1100, 0x0229: 0x1100, - 0x022a: 0x9900, 0x022b: 0x9900, 0x022c: 0x1100, 0x022d: 0x1100, 0x022e: 0x1100, 0x022f: 0x1100, - 0x0230: 0x1100, 0x0231: 0x3000, 0x0232: 0x3000, 0x0233: 0x3000, 0x0234: 0x1100, 0x0235: 0x1100, - 0x0238: 0x1100, 0x0239: 0x1100, 0x023a: 0x1100, 0x023b: 0x1100, - 0x023c: 0x1100, 0x023d: 0x1100, 0x023e: 0x1100, 0x023f: 0x1100, + 0x0207: 0x3000, + 0x0211: 0x00dc, + 0x0212: 0x00e6, 0x0213: 0x00e6, 0x0214: 0x00e6, 0x0215: 0x00e6, 0x0216: 0x00dc, 0x0217: 0x00e6, + 0x0218: 0x00e6, 0x0219: 0x00e6, 0x021a: 0x00de, 0x021b: 0x00dc, 0x021c: 0x00e6, 0x021d: 0x00e6, + 0x021e: 0x00e6, 0x021f: 0x00e6, 0x0220: 0x00e6, 0x0221: 0x00e6, 0x0222: 0x00dc, 0x0223: 0x00dc, + 0x0224: 0x00dc, 0x0225: 0x00dc, 0x0226: 0x00dc, 0x0227: 0x00dc, 0x0228: 0x00e6, 0x0229: 0x00e6, + 0x022a: 0x00dc, 0x022b: 0x00e6, 0x022c: 0x00e6, 0x022d: 0x00de, 0x022e: 0x00e4, 0x022f: 0x00e6, + 0x0230: 0x000a, 0x0231: 0x000b, 0x0232: 0x000c, 0x0233: 0x000d, 0x0234: 0x000e, 0x0235: 0x000f, + 0x0236: 0x0010, 0x0237: 0x0011, 0x0238: 0x0012, 0x0239: 0x0013, 0x023a: 0x0013, 0x023b: 0x0014, + 0x023c: 0x0015, 0x023d: 0x0016, 0x023f: 0x0017, // Block 0x9, offset 0x240 - 0x0240: 0x1100, 0x0241: 0x1100, 0x0242: 0x1100, 0x0243: 0x1100, 0x0244: 0x1100, 0x0245: 0x1100, - 0x0246: 0x1100, 0x0247: 0x1100, 0x0248: 0x1100, 0x0249: 0x1100, 0x024a: 0x1100, 0x024b: 0x1100, - 0x024c: 0x1100, 0x024d: 0x1100, 0x024e: 0x1100, 0x024f: 0x1100, 0x0250: 0x1100, 0x0251: 0x1100, - 0x0252: 0x1100, 0x0253: 0x1100, 0x0254: 0x1100, 0x0255: 0x1100, 0x0256: 0x1100, 0x0257: 0x1100, - 0x0258: 0x1100, 0x0259: 0x1100, 0x025a: 0x1100, 0x025b: 0x1100, - 0x025e: 0x1100, 0x025f: 0x1100, - 0x0266: 0x9900, 0x0267: 0x9900, 0x0268: 0x9900, 0x0269: 0x9900, - 0x026a: 0x1100, 0x026b: 0x1100, 0x026c: 0x1100, 0x026d: 0x1100, 0x026e: 0x9900, 0x026f: 0x9900, - 0x0270: 0x1100, 0x0271: 0x1100, 0x0272: 0x1100, 0x0273: 0x1100, + 0x0248: 0x8800, 0x024a: 0x8800, 0x024b: 0x001b, + 0x024c: 0x001c, 0x024d: 0x001d, 0x024e: 0x001e, 0x024f: 0x001f, 0x0250: 0x0020, 0x0251: 0x0021, + 0x0252: 0x0022, 0x0253: 0x66e6, 0x0254: 0x66e6, 0x0255: 0x66dc, 0x0256: 0x00dc, 0x0257: 0x00e6, + 0x0258: 0x00e6, 0x0259: 0x00e6, 0x025a: 0x00e6, 0x025b: 0x00e6, 0x025c: 0x00dc, 0x025d: 0x00e6, + 0x025e: 0x00e6, 0x025f: 0x00dc, + 0x0270: 0x0023, 0x0275: 0x3000, + 0x0276: 0x3000, 0x0277: 0x3000, 0x0278: 0x3000, // Block 0xa, offset 0x280 - 0x0292: 0x8800, - 0x02b0: 0x3000, 0x02b1: 0x3000, 0x02b2: 0x3000, 0x02b3: 0x3000, 0x02b4: 0x3000, 0x02b5: 0x3000, - 0x02b6: 0x3000, 0x02b7: 0x3000, 0x02b8: 0x3000, + 0x0280: 0x9900, 0x0281: 0x9900, 0x0282: 0x1100, 0x0283: 0x1100, 0x0284: 0x1100, 0x0285: 0x1100, + 0x0288: 0x9900, 0x0289: 0x9900, 0x028a: 0x1100, 0x028b: 0x1100, + 0x028c: 0x1100, 0x028d: 0x1100, 0x0290: 0x9900, 0x0291: 0x9900, + 0x0292: 0x1100, 0x0293: 0x1100, 0x0294: 0x1100, 0x0295: 0x1100, 0x0296: 0x1100, 0x0297: 0x1100, + 0x0299: 0x9900, 0x029b: 0x1100, 0x029d: 0x1100, + 0x029f: 0x1100, 0x02a0: 0x9900, 0x02a1: 0x9900, 0x02a2: 0x9900, 0x02a3: 0x9900, + 0x02a4: 0x9900, 0x02a5: 0x9900, 0x02a6: 0x9900, 0x02a7: 0x9900, 0x02a8: 0x9900, 0x02a9: 0x9900, + 0x02aa: 0x9900, 0x02ab: 0x9900, 0x02ac: 0x9900, 0x02ad: 0x9900, 0x02ae: 0x9900, 0x02af: 0x9900, + 0x02b0: 0x9900, 0x02b1: 0x3300, 0x02b2: 0x1100, 0x02b3: 0x3300, 0x02b4: 0x9900, 0x02b5: 0x3300, + 0x02b6: 0x1100, 0x02b7: 0x3300, 0x02b8: 0x1100, 0x02b9: 0x3300, 0x02ba: 0x1100, 0x02bb: 0x3300, + 0x02bc: 0x9900, 0x02bd: 0x3300, // Block 0xb, offset 0x2c0 - 0x02d8: 0x3000, 0x02d9: 0x3000, 0x02da: 0x3000, 0x02db: 0x3000, 0x02dc: 0x3000, 0x02dd: 0x3000, - 0x02e0: 0x3000, 0x02e1: 0x3000, 0x02e2: 0x3000, 0x02e3: 0x3000, - 0x02e4: 0x3000, + 0x02c0: 0x3000, 0x02c1: 0x3100, 0x02c2: 0x1100, 0x02c3: 0x1100, 0x02c4: 0x1100, + 0x02c6: 0x9900, 0x02c7: 0x1100, 0x02c8: 0x1100, 0x02c9: 0x3300, 0x02ca: 0x1100, 0x02cb: 0x3300, + 0x02cc: 0x1100, 0x02cd: 0x3100, 0x02ce: 0x3100, 0x02cf: 0x3100, 0x02d0: 0x1100, 0x02d1: 0x1100, + 0x02d2: 0x1100, 0x02d3: 0x3300, 0x02d6: 0x1100, 0x02d7: 0x1100, + 0x02d8: 0x1100, 0x02d9: 0x1100, 0x02da: 0x1100, 0x02db: 0x3300, 0x02dd: 0x3100, + 0x02de: 0x3100, 0x02df: 0x3100, 0x02e0: 0x1100, 0x02e1: 0x1100, 0x02e2: 0x1100, 0x02e3: 0x3300, + 0x02e4: 0x1100, 0x02e5: 0x1100, 0x02e6: 0x1100, 0x02e7: 0x1100, 0x02e8: 0x1100, 0x02e9: 0x1100, + 0x02ea: 0x1100, 0x02eb: 0x3300, 0x02ec: 0x1100, 0x02ed: 0x3100, 0x02ee: 0x3300, 0x02ef: 0x3300, + 0x02f2: 0x1100, 0x02f3: 0x1100, 0x02f4: 0x1100, + 0x02f6: 0x9900, 0x02f7: 0x1100, 0x02f8: 0x1100, 0x02f9: 0x3300, 0x02fa: 0x1100, 0x02fb: 0x3300, + 0x02fc: 0x1100, 0x02fd: 0x3300, 0x02fe: 0x3800, // Block 0xc, offset 0x300 - 0x0300: 0x66e6, 0x0301: 0x66e6, 0x0302: 0x66e6, 0x0303: 0x66e6, 0x0304: 0x66e6, 0x0305: 0x00e6, - 0x0306: 0x66e6, 0x0307: 0x66e6, 0x0308: 0x66e6, 0x0309: 0x66e6, 0x030a: 0x66e6, 0x030b: 0x66e6, - 0x030c: 0x66e6, 0x030d: 0x00e6, 0x030e: 0x00e6, 0x030f: 0x66e6, 0x0310: 0x00e6, 0x0311: 0x66e6, - 0x0312: 0x00e6, 0x0313: 0x66e6, 0x0314: 0x66e6, 0x0315: 0x00e8, 0x0316: 0x00dc, 0x0317: 0x00dc, - 0x0318: 0x00dc, 0x0319: 0x00dc, 0x031a: 0x00e8, 0x031b: 0x66d8, 0x031c: 0x00dc, 0x031d: 0x00dc, - 0x031e: 0x00dc, 0x031f: 0x00dc, 0x0320: 0x00dc, 0x0321: 0x00ca, 0x0322: 0x00ca, 0x0323: 0x66dc, - 0x0324: 0x66dc, 0x0325: 0x66dc, 0x0326: 0x66dc, 0x0327: 0x66ca, 0x0328: 0x66ca, 0x0329: 0x00dc, - 0x032a: 0x00dc, 0x032b: 0x00dc, 0x032c: 0x00dc, 0x032d: 0x66dc, 0x032e: 0x66dc, 0x032f: 0x00dc, - 0x0330: 0x66dc, 0x0331: 0x66dc, 0x0332: 0x00dc, 0x0333: 0x00dc, 0x0334: 0x0001, 0x0335: 0x0001, - 0x0336: 0x0001, 0x0337: 0x0001, 0x0338: 0x6601, 0x0339: 0x00dc, 0x033a: 0x00dc, 0x033b: 0x00dc, - 0x033c: 0x00dc, 0x033d: 0x00e6, 0x033e: 0x00e6, 0x033f: 0x00e6, + 0x0301: 0x1100, 0x0303: 0x8800, 0x0304: 0x1100, 0x0305: 0x8800, + 0x0307: 0x1100, 0x0308: 0x8800, 0x0309: 0x1100, + 0x030d: 0x8800, + 0x0320: 0x1100, 0x0321: 0x8800, 0x0322: 0x1100, + 0x0324: 0x8800, 0x0325: 0x8800, + 0x032d: 0x1100, 0x032e: 0x1100, 0x032f: 0x1100, + 0x0330: 0x1100, 0x0331: 0x1100, 0x0332: 0x8800, 0x0333: 0x8800, 0x0334: 0x1100, 0x0335: 0x1100, + 0x0336: 0x8800, 0x0337: 0x8800, 0x0338: 0x1100, 0x0339: 0x1100, 0x033a: 0x8800, 0x033b: 0x8800, + 0x033c: 0x8800, 0x033d: 0x8800, // Block 0xd, offset 0x340 - 0x0340: 0x33e6, 0x0341: 0x33e6, 0x0342: 0x66e6, 0x0343: 0x33e6, 0x0344: 0x33e6, 0x0345: 0x66f0, - 0x0346: 0x00e6, 0x0347: 0x00dc, 0x0348: 0x00dc, 0x0349: 0x00dc, 0x034a: 0x00e6, 0x034b: 0x00e6, - 0x034c: 0x00e6, 0x034d: 0x00dc, 0x034e: 0x00dc, 0x0350: 0x00e6, 0x0351: 0x00e6, - 0x0352: 0x00e6, 0x0353: 0x00dc, 0x0354: 0x00dc, 0x0355: 0x00dc, 0x0356: 0x00dc, 0x0357: 0x00e6, - 0x0358: 0x00e8, 0x0359: 0x00dc, 0x035a: 0x00dc, 0x035b: 0x00e6, 0x035c: 0x00e9, 0x035d: 0x00ea, - 0x035e: 0x00ea, 0x035f: 0x00e9, 0x0360: 0x00ea, 0x0361: 0x00ea, 0x0362: 0x00e9, 0x0363: 0x00e6, - 0x0364: 0x00e6, 0x0365: 0x00e6, 0x0366: 0x00e6, 0x0367: 0x00e6, 0x0368: 0x00e6, 0x0369: 0x00e6, - 0x036a: 0x00e6, 0x036b: 0x00e6, 0x036c: 0x00e6, 0x036d: 0x00e6, 0x036e: 0x00e6, 0x036f: 0x00e6, - 0x0374: 0x3300, - 0x037a: 0x3000, - 0x037e: 0x3300, + 0x0346: 0x8800, 0x034b: 0x8800, + 0x034c: 0x1100, 0x034d: 0x8800, 0x034e: 0x1100, 0x034f: 0x8800, 0x0350: 0x1100, 0x0351: 0x8800, + 0x0352: 0x1100, 0x0353: 0x8800, 0x0354: 0x1100, 0x0355: 0x8800, 0x0356: 0x1100, 0x0357: 0x8800, + 0x0358: 0x1100, 0x0359: 0x8800, 0x035a: 0x1100, 0x035b: 0x8800, 0x035c: 0x1100, 0x035d: 0x8800, + 0x035e: 0x1100, 0x035f: 0x8800, 0x0360: 0x1100, 0x0361: 0x8800, 0x0362: 0x1100, + 0x0364: 0x8800, 0x0365: 0x1100, 0x0366: 0x8800, 0x0367: 0x1100, 0x0368: 0x8800, 0x0369: 0x1100, + 0x036f: 0x8800, + 0x0370: 0x1100, 0x0371: 0x1100, 0x0372: 0x8800, 0x0373: 0x1100, 0x0374: 0x1100, 0x0375: 0x8800, + 0x0376: 0x1100, 0x0377: 0x1100, 0x0378: 0x8800, 0x0379: 0x1100, 0x037a: 0x1100, 0x037b: 0x8800, + 0x037c: 0x1100, 0x037d: 0x1100, // Block 0xe, offset 0x380 - 0x0384: 0x3000, 0x0385: 0x3100, - 0x0386: 0x1100, 0x0387: 0x3300, 0x0388: 0x1100, 0x0389: 0x1100, 0x038a: 0x1100, - 0x038c: 0x1100, 0x038e: 0x1100, 0x038f: 0x1100, 0x0390: 0x1100, 0x0391: 0x8800, - 0x0395: 0x8800, 0x0397: 0x8800, - 0x0399: 0x8800, - 0x039f: 0x8800, 0x03a1: 0x8800, - 0x03a5: 0x8800, 0x03a9: 0x8800, - 0x03aa: 0x1100, 0x03ab: 0x1100, 0x03ac: 0x9900, 0x03ad: 0x1100, 0x03ae: 0x9900, 0x03af: 0x1100, - 0x03b0: 0x1100, 0x03b1: 0x8800, 0x03b5: 0x8800, - 0x03b7: 0x8800, 0x03b9: 0x8800, - 0x03bf: 0x8800, + 0x0394: 0x1100, + 0x0399: 0x6608, 0x039a: 0x6608, 0x039b: 0x3000, 0x039c: 0x3000, 0x039d: 0x8800, + 0x039e: 0x1100, 0x039f: 0x3000, + 0x03a6: 0x8800, + 0x03ab: 0x8800, 0x03ac: 0x1100, 0x03ad: 0x8800, 0x03ae: 0x1100, 0x03af: 0x8800, + 0x03b0: 0x1100, 0x03b1: 0x8800, 0x03b2: 0x1100, 0x03b3: 0x8800, 0x03b4: 0x1100, 0x03b5: 0x8800, + 0x03b6: 0x1100, 0x03b7: 0x8800, 0x03b8: 0x1100, 0x03b9: 0x8800, 0x03ba: 0x1100, 0x03bb: 0x8800, + 0x03bc: 0x1100, 0x03bd: 0x8800, 0x03be: 0x1100, 0x03bf: 0x8800, // Block 0xf, offset 0x3c0 - 0x03c1: 0x8800, 0x03c5: 0x8800, - 0x03c9: 0x8800, 0x03ca: 0x9900, 0x03cb: 0x9900, - 0x03cc: 0x1100, 0x03cd: 0x1100, 0x03ce: 0x9900, 0x03d0: 0x3000, 0x03d1: 0x3000, - 0x03d2: 0x3800, 0x03d3: 0x3100, 0x03d4: 0x3100, 0x03d5: 0x3000, 0x03d6: 0x3000, - 0x03f0: 0x3000, 0x03f1: 0x3000, 0x03f2: 0x3000, 0x03f4: 0x3000, 0x03f5: 0x3000, - 0x03f9: 0x3000, - // Block 0x10, offset 0x400 - 0x0400: 0x1100, 0x0401: 0x1100, 0x0403: 0x1100, - 0x0406: 0x8800, 0x0407: 0x1100, - 0x040c: 0x1100, 0x040d: 0x1100, 0x040e: 0x1100, 0x0410: 0x8800, - 0x0413: 0x8800, 0x0415: 0x8800, 0x0416: 0x8800, 0x0417: 0x8800, - 0x0418: 0x8800, 0x0419: 0x1100, 0x041a: 0x8800, - 0x041e: 0x8800, 0x0423: 0x8800, - 0x0427: 0x8800, - 0x042b: 0x8800, 0x042d: 0x8800, - 0x0430: 0x8800, 0x0433: 0x8800, 0x0435: 0x8800, - 0x0436: 0x8800, 0x0437: 0x8800, 0x0438: 0x8800, 0x0439: 0x1100, 0x043a: 0x8800, - 0x043e: 0x8800, - // Block 0x11, offset 0x440 - 0x0443: 0x8800, - 0x0447: 0x8800, 0x044b: 0x8800, - 0x044d: 0x8800, 0x0450: 0x1100, 0x0451: 0x1100, - 0x0453: 0x1100, 0x0456: 0x8800, 0x0457: 0x1100, - 0x045c: 0x1100, 0x045d: 0x1100, - 0x045e: 0x1100, - 0x0474: 0x8800, 0x0475: 0x8800, - 0x0476: 0x1100, 0x0477: 0x1100, - // Block 0x12, offset 0x480 - 0x0483: 0x00e6, 0x0484: 0x00e6, 0x0485: 0x00e6, - 0x0486: 0x00e6, 0x0487: 0x00e6, - // Block 0x13, offset 0x4c0 - 0x04c1: 0x1100, 0x04c2: 0x1100, - 0x04d0: 0x1100, 0x04d1: 0x1100, - 0x04d2: 0x1100, 0x04d3: 0x1100, 0x04d6: 0x1100, 0x04d7: 0x1100, - 0x04d8: 0x8800, 0x04d9: 0x8800, 0x04da: 0x1100, 0x04db: 0x1100, 0x04dc: 0x1100, 0x04dd: 0x1100, - 0x04de: 0x1100, 0x04df: 0x1100, 0x04e2: 0x1100, 0x04e3: 0x1100, - 0x04e4: 0x1100, 0x04e5: 0x1100, 0x04e6: 0x1100, 0x04e7: 0x1100, 0x04e8: 0x8800, 0x04e9: 0x8800, - 0x04ea: 0x1100, 0x04eb: 0x1100, 0x04ec: 0x1100, 0x04ed: 0x1100, 0x04ee: 0x1100, 0x04ef: 0x1100, - 0x04f0: 0x1100, 0x04f1: 0x1100, 0x04f2: 0x1100, 0x04f3: 0x1100, 0x04f4: 0x1100, 0x04f5: 0x1100, - 0x04f8: 0x1100, 0x04f9: 0x1100, - // Block 0x14, offset 0x500 - 0x0507: 0x3000, - 0x0511: 0x00dc, - 0x0512: 0x00e6, 0x0513: 0x00e6, 0x0514: 0x00e6, 0x0515: 0x00e6, 0x0516: 0x00dc, 0x0517: 0x00e6, - 0x0518: 0x00e6, 0x0519: 0x00e6, 0x051a: 0x00de, 0x051b: 0x00dc, 0x051c: 0x00e6, 0x051d: 0x00e6, - 0x051e: 0x00e6, 0x051f: 0x00e6, 0x0520: 0x00e6, 0x0521: 0x00e6, 0x0522: 0x00dc, 0x0523: 0x00dc, - 0x0524: 0x00dc, 0x0525: 0x00dc, 0x0526: 0x00dc, 0x0527: 0x00dc, 0x0528: 0x00e6, 0x0529: 0x00e6, - 0x052a: 0x00dc, 0x052b: 0x00e6, 0x052c: 0x00e6, 0x052d: 0x00de, 0x052e: 0x00e4, 0x052f: 0x00e6, - 0x0530: 0x000a, 0x0531: 0x000b, 0x0532: 0x000c, 0x0533: 0x000d, 0x0534: 0x000e, 0x0535: 0x000f, - 0x0536: 0x0010, 0x0537: 0x0011, 0x0538: 0x0012, 0x0539: 0x0013, 0x053a: 0x0013, 0x053b: 0x0014, - 0x053c: 0x0015, 0x053d: 0x0016, 0x053f: 0x0017, - // Block 0x15, offset 0x540 - 0x0541: 0x0018, 0x0542: 0x0019, 0x0544: 0x00e6, 0x0545: 0x00dc, - 0x0547: 0x0012, - // Block 0x16, offset 0x580 - 0x0590: 0x00e6, 0x0591: 0x00e6, - 0x0592: 0x00e6, 0x0593: 0x00e6, 0x0594: 0x00e6, 0x0595: 0x00e6, 0x0596: 0x00e6, 0x0597: 0x00e6, - 0x0598: 0x001e, 0x0599: 0x001f, 0x059a: 0x0020, - 0x05a2: 0x1100, 0x05a3: 0x1100, - 0x05a4: 0x1100, 0x05a5: 0x1100, 0x05a6: 0x1100, 0x05a7: 0x8800, - // Block 0x17, offset 0x5c0 - 0x05c8: 0x8800, 0x05ca: 0x8800, 0x05cb: 0x001b, - 0x05cc: 0x001c, 0x05cd: 0x001d, 0x05ce: 0x001e, 0x05cf: 0x001f, 0x05d0: 0x0020, 0x05d1: 0x0021, - 0x05d2: 0x0022, 0x05d3: 0x66e6, 0x05d4: 0x66e6, 0x05d5: 0x66dc, 0x05d6: 0x00dc, 0x05d7: 0x00e6, - 0x05d8: 0x00e6, 0x05d9: 0x00e6, 0x05da: 0x00e6, 0x05db: 0x00e6, 0x05dc: 0x00dc, 0x05dd: 0x00e6, - 0x05de: 0x00e6, 0x05df: 0x00dc, - 0x05f0: 0x0023, 0x05f5: 0x3000, - 0x05f6: 0x3000, 0x05f7: 0x3000, 0x05f8: 0x3000, - // Block 0x18, offset 0x600 - 0x0600: 0x1100, 0x0601: 0x8800, 0x0602: 0x1100, - 0x0612: 0x8800, 0x0613: 0x1100, 0x0615: 0x8800, 0x0616: 0x00e6, 0x0617: 0x00e6, - 0x0618: 0x00e6, 0x0619: 0x00e6, 0x061a: 0x00e6, 0x061b: 0x00e6, 0x061c: 0x00e6, - 0x061f: 0x00e6, 0x0620: 0x00e6, 0x0621: 0x00e6, 0x0622: 0x00e6, 0x0623: 0x00dc, - 0x0624: 0x00e6, 0x0627: 0x00e6, 0x0628: 0x00e6, - 0x062a: 0x00dc, 0x062b: 0x00e6, 0x062c: 0x00e6, 0x062d: 0x00dc, - // Block 0x19, offset 0x640 - 0x0651: 0x0024, - 0x0670: 0x00e6, 0x0671: 0x00dc, 0x0672: 0x00e6, 0x0673: 0x00e6, 0x0674: 0x00dc, 0x0675: 0x00e6, - 0x0676: 0x00e6, 0x0677: 0x00dc, 0x0678: 0x00dc, 0x0679: 0x00dc, 0x067a: 0x00e6, 0x067b: 0x00dc, - 0x067c: 0x00dc, 0x067d: 0x00e6, 0x067e: 0x00dc, 0x067f: 0x00e6, - // Block 0x1a, offset 0x680 - 0x0680: 0x00e6, 0x0681: 0x00e6, 0x0682: 0x00dc, 0x0683: 0x00e6, 0x0684: 0x00dc, 0x0685: 0x00e6, - 0x0686: 0x00dc, 0x0687: 0x00e6, 0x0688: 0x00dc, 0x0689: 0x00e6, 0x068a: 0x00e6, - // Block 0x1b, offset 0x6c0 - 0x06eb: 0x00e6, 0x06ec: 0x00e6, 0x06ed: 0x00e6, 0x06ee: 0x00e6, 0x06ef: 0x00e6, - 0x06f0: 0x00e6, 0x06f1: 0x00e6, 0x06f2: 0x00dc, 0x06f3: 0x00e6, - // Block 0x1c, offset 0x700 - 0x0716: 0x00e6, 0x0717: 0x00e6, - 0x0718: 0x00e6, 0x0719: 0x00e6, 0x071b: 0x00e6, 0x071c: 0x00e6, 0x071d: 0x00e6, - 0x071e: 0x00e6, 0x071f: 0x00e6, 0x0720: 0x00e6, 0x0721: 0x00e6, 0x0722: 0x00e6, 0x0723: 0x00e6, - 0x0725: 0x00e6, 0x0726: 0x00e6, 0x0727: 0x00e6, 0x0729: 0x00e6, - 0x072a: 0x00e6, 0x072b: 0x00e6, 0x072c: 0x00e6, 0x072d: 0x00e6, - // Block 0x1d, offset 0x740 - 0x0759: 0x00dc, 0x075a: 0x00dc, 0x075b: 0x00dc, - // Block 0x1e, offset 0x780 - 0x07a8: 0x8800, 0x07a9: 0x1100, - 0x07b0: 0x8800, 0x07b1: 0x1100, 0x07b3: 0x8800, 0x07b4: 0x1100, - 0x07bc: 0x6607, - // Block 0x1f, offset 0x7c0 - 0x07cd: 0x0009, 0x07d1: 0x00e6, - 0x07d2: 0x00dc, 0x07d3: 0x00e6, 0x07d4: 0x00e6, - 0x07d8: 0x3300, 0x07d9: 0x3300, 0x07da: 0x3300, 0x07db: 0x3300, 0x07dc: 0x3300, 0x07dd: 0x3300, - 0x07de: 0x3300, 0x07df: 0x3300, - // Block 0x20, offset 0x800 - 0x083c: 0x0007, 0x083e: 0x6600, - // Block 0x21, offset 0x840 - 0x0847: 0x8800, 0x084b: 0x1100, - 0x084c: 0x1100, 0x084d: 0x0009, - 0x0857: 0x6600, - 0x085c: 0x3300, 0x085d: 0x3300, - 0x085f: 0x3300, - // Block 0x22, offset 0x880 - 0x08b3: 0x3300, - 0x08b6: 0x3300, - 0x08bc: 0x0007, - // Block 0x23, offset 0x8c0 - 0x08cd: 0x0009, - 0x08d9: 0x3300, 0x08da: 0x3300, 0x08db: 0x3300, - 0x08de: 0x3300, - // Block 0x24, offset 0x900 - 0x093c: 0x0007, - // Block 0x25, offset 0x940 - 0x094d: 0x0009, - // Block 0x26, offset 0x980 - 0x0987: 0x8800, 0x0988: 0x1100, 0x098b: 0x1100, - 0x098c: 0x1100, 0x098d: 0x0009, - 0x0996: 0x6600, 0x0997: 0x6600, - 0x099c: 0x3300, 0x099d: 0x3300, - // Block 0x27, offset 0x9c0 - 0x09d2: 0x8800, 0x09d4: 0x1100, - 0x09fe: 0x6600, - // Block 0x28, offset 0xa00 - 0x0a06: 0x8800, 0x0a07: 0x8800, 0x0a0a: 0x1100, 0x0a0b: 0x1100, - 0x0a0c: 0x1100, 0x0a0d: 0x0009, - 0x0a17: 0x6600, - // Block 0x29, offset 0xa40 - 0x0a46: 0x8800, 0x0a48: 0x1100, - 0x0a4d: 0x0009, - 0x0a55: 0x0054, 0x0a56: 0x665b, - // Block 0x2a, offset 0xa80 - 0x0abc: 0x0007, 0x0abf: 0x8800, - // Block 0x2b, offset 0xac0 - 0x0ac0: 0x1100, 0x0ac2: 0x6600, - 0x0ac6: 0x8800, 0x0ac7: 0x1100, 0x0ac8: 0x1100, 0x0aca: 0x9900, 0x0acb: 0x1100, - 0x0acd: 0x0009, - 0x0ad5: 0x6600, 0x0ad6: 0x6600, - // Block 0x2c, offset 0xb00 - 0x0b3e: 0x6600, - // Block 0x2d, offset 0xb40 - 0x0b4a: 0x6609, - 0x0b4f: 0x6600, - 0x0b59: 0x8800, 0x0b5a: 0x1100, 0x0b5c: 0x9900, 0x0b5d: 0x1100, - 0x0b5e: 0x1100, 0x0b5f: 0x6600, - // Block 0x2e, offset 0xb80 - 0x0bb3: 0x3000, - 0x0bb8: 0x0067, 0x0bb9: 0x0067, 0x0bba: 0x0009, - // Block 0x2f, offset 0xbc0 - 0x0bc8: 0x006b, 0x0bc9: 0x006b, 0x0bca: 0x006b, 0x0bcb: 0x006b, - // Block 0x30, offset 0xc00 - 0x0c33: 0x3000, - 0x0c38: 0x0076, 0x0c39: 0x0076, - // Block 0x31, offset 0xc40 - 0x0c48: 0x007a, 0x0c49: 0x007a, 0x0c4a: 0x007a, 0x0c4b: 0x007a, - 0x0c5c: 0x3000, 0x0c5d: 0x3000, - // Block 0x32, offset 0xc80 - 0x0c8c: 0x3000, - 0x0c98: 0x00dc, 0x0c99: 0x00dc, - 0x0cb5: 0x00dc, - 0x0cb7: 0x00dc, 0x0cb9: 0x00d8, - // Block 0x33, offset 0xcc0 - 0x0cc3: 0x3300, - 0x0ccd: 0x3300, - 0x0cd2: 0x3300, 0x0cd7: 0x3300, - 0x0cdc: 0x3300, - 0x0ce9: 0x3300, - 0x0cf1: 0x0081, 0x0cf2: 0x0082, 0x0cf3: 0x3300, 0x0cf4: 0x0084, 0x0cf5: 0x3300, - 0x0cf6: 0x3300, 0x0cf7: 0x3000, 0x0cf8: 0x3300, 0x0cf9: 0x3000, 0x0cfa: 0x0082, 0x0cfb: 0x0082, - 0x0cfc: 0x0082, 0x0cfd: 0x0082, - // Block 0x34, offset 0xd00 - 0x0d00: 0x0082, 0x0d01: 0x3300, 0x0d02: 0x00e6, 0x0d03: 0x00e6, 0x0d04: 0x0009, - 0x0d06: 0x00e6, 0x0d07: 0x00e6, - 0x0d13: 0x3300, - 0x0d1d: 0x3300, - 0x0d22: 0x3300, - 0x0d27: 0x3300, - 0x0d2c: 0x3300, - 0x0d39: 0x3300, - // Block 0x35, offset 0xd40 - 0x0d46: 0x00dc, - // Block 0x36, offset 0xd80 - 0x0da5: 0x8800, 0x0da6: 0x1100, - 0x0dae: 0x6600, - 0x0db7: 0x0007, 0x0db9: 0x0009, 0x0dba: 0x0009, - // Block 0x37, offset 0xdc0 - 0x0dcd: 0x00dc, - // Block 0x38, offset 0xe00 - 0x0e3c: 0x3000, - // Block 0x39, offset 0xe40 - 0x0e61: 0x6600, 0x0e62: 0x6600, 0x0e63: 0x6600, - 0x0e64: 0x6600, 0x0e65: 0x6600, 0x0e66: 0x6600, 0x0e67: 0x6600, 0x0e68: 0x6600, 0x0e69: 0x6600, - 0x0e6a: 0x6600, 0x0e6b: 0x6600, 0x0e6c: 0x6600, 0x0e6d: 0x6600, 0x0e6e: 0x6600, 0x0e6f: 0x6600, - 0x0e70: 0x6600, 0x0e71: 0x6600, 0x0e72: 0x6600, 0x0e73: 0x6600, 0x0e74: 0x6600, 0x0e75: 0x6600, - // Block 0x3a, offset 0xe80 - 0x0ea8: 0x6600, 0x0ea9: 0x6600, - 0x0eaa: 0x6600, 0x0eab: 0x6600, 0x0eac: 0x6600, 0x0ead: 0x6600, 0x0eae: 0x6600, 0x0eaf: 0x6600, - 0x0eb0: 0x6600, 0x0eb1: 0x6600, 0x0eb2: 0x6600, 0x0eb3: 0x6600, 0x0eb4: 0x6600, 0x0eb5: 0x6600, - 0x0eb6: 0x6600, 0x0eb7: 0x6600, 0x0eb8: 0x6600, 0x0eb9: 0x6600, 0x0eba: 0x6600, 0x0ebb: 0x6600, - 0x0ebc: 0x6600, 0x0ebd: 0x6600, 0x0ebe: 0x6600, 0x0ebf: 0x6600, - // Block 0x3b, offset 0xec0 - 0x0ec0: 0x6600, 0x0ec1: 0x6600, 0x0ec2: 0x6600, - // Block 0x3c, offset 0xf00 - 0x0f1d: 0x00e6, - 0x0f1e: 0x00e6, 0x0f1f: 0x00e6, - // Block 0x3d, offset 0xf40 - 0x0f54: 0x0009, - 0x0f74: 0x0009, - // Block 0x3e, offset 0xf80 - 0x0f92: 0x0009, - 0x0f9d: 0x00e6, - // Block 0x3f, offset 0xfc0 - 0x0fe9: 0x00e4, - // Block 0x40, offset 0x1000 - 0x1039: 0x00de, 0x103a: 0x00e6, 0x103b: 0x00dc, - // Block 0x41, offset 0x1040 - 0x1057: 0x00e6, - 0x1058: 0x00dc, - // Block 0x42, offset 0x1080 - 0x10a0: 0x0009, - 0x10b5: 0x00e6, - 0x10b6: 0x00e6, 0x10b7: 0x00e6, 0x10b8: 0x00e6, 0x10b9: 0x00e6, 0x10ba: 0x00e6, 0x10bb: 0x00e6, - 0x10bc: 0x00e6, 0x10bf: 0x00dc, - // Block 0x43, offset 0x10c0 - 0x10c5: 0x8800, - 0x10c6: 0x1100, 0x10c7: 0x8800, 0x10c8: 0x1100, 0x10c9: 0x8800, 0x10ca: 0x1100, 0x10cb: 0x8800, - 0x10cc: 0x1100, 0x10cd: 0x8800, 0x10ce: 0x1100, 0x10d1: 0x8800, - 0x10d2: 0x1100, - 0x10f4: 0x0007, 0x10f5: 0x6600, - 0x10fa: 0x8800, 0x10fb: 0x1100, - 0x10fc: 0x8800, 0x10fd: 0x1100, 0x10fe: 0x8800, 0x10ff: 0x8800, - // Block 0x44, offset 0x1100 - 0x1100: 0x1100, 0x1101: 0x1100, 0x1102: 0x8800, 0x1103: 0x1100, 0x1104: 0x0009, - 0x112b: 0x00e6, 0x112c: 0x00dc, 0x112d: 0x00e6, 0x112e: 0x00e6, 0x112f: 0x00e6, - 0x1130: 0x00e6, 0x1131: 0x00e6, 0x1132: 0x00e6, 0x1133: 0x00e6, - // Block 0x45, offset 0x1140 - 0x116a: 0x0009, - // Block 0x46, offset 0x1180 - 0x11a6: 0x0007, - 0x11b2: 0x0009, 0x11b3: 0x0009, - // Block 0x47, offset 0x11c0 - 0x11f7: 0x0007, - // Block 0x48, offset 0x1200 - 0x1210: 0x00e6, 0x1211: 0x00e6, - 0x1212: 0x00e6, 0x1214: 0x0001, 0x1215: 0x00dc, 0x1216: 0x00dc, 0x1217: 0x00dc, - 0x1218: 0x00dc, 0x1219: 0x00dc, 0x121a: 0x00e6, 0x121b: 0x00e6, 0x121c: 0x00dc, 0x121d: 0x00dc, - 0x121e: 0x00dc, 0x121f: 0x00dc, 0x1220: 0x00e6, 0x1222: 0x0001, 0x1223: 0x0001, - 0x1224: 0x0001, 0x1225: 0x0001, 0x1226: 0x0001, 0x1227: 0x0001, 0x1228: 0x0001, - 0x122d: 0x00dc, - // Block 0x49, offset 0x1240 - 0x126c: 0x3000, 0x126d: 0x3000, 0x126e: 0x3000, - 0x1270: 0x3000, 0x1271: 0x3000, 0x1272: 0x3000, 0x1273: 0x3000, 0x1274: 0x3000, 0x1275: 0x3000, - 0x1276: 0x3000, 0x1277: 0x3000, 0x1278: 0x3000, 0x1279: 0x3000, 0x127a: 0x3000, - 0x127c: 0x3000, 0x127d: 0x3000, 0x127e: 0x3000, 0x127f: 0x3000, - // Block 0x4a, offset 0x1280 - 0x1280: 0x3000, 0x1281: 0x3000, 0x1282: 0x3000, 0x1283: 0x3000, 0x1284: 0x3000, 0x1285: 0x3000, - 0x1286: 0x3000, 0x1287: 0x3000, 0x1288: 0x3000, 0x1289: 0x3000, 0x128a: 0x3000, 0x128b: 0x3000, - 0x128c: 0x3000, 0x128d: 0x3000, 0x128f: 0x3000, 0x1290: 0x3000, 0x1291: 0x3000, - 0x1292: 0x3000, 0x1293: 0x3000, 0x1294: 0x3000, 0x1295: 0x3000, 0x1296: 0x3000, 0x1297: 0x3000, - 0x1298: 0x3000, 0x1299: 0x3000, 0x129a: 0x3000, 0x129b: 0x3000, 0x129c: 0x3000, 0x129d: 0x3000, - 0x129e: 0x3000, 0x129f: 0x3000, 0x12a0: 0x3000, 0x12a1: 0x3000, 0x12a2: 0x3000, 0x12a3: 0x3000, - 0x12a4: 0x3000, 0x12a5: 0x3000, 0x12a6: 0x3000, 0x12a7: 0x3000, 0x12a8: 0x3000, 0x12a9: 0x3000, - 0x12aa: 0x3000, - 0x12b8: 0x3000, - // Block 0x4b, offset 0x12c0 - 0x12db: 0x3000, 0x12dc: 0x3000, 0x12dd: 0x3000, - 0x12de: 0x3000, 0x12df: 0x3000, 0x12e0: 0x3000, 0x12e1: 0x3000, 0x12e2: 0x3000, 0x12e3: 0x3000, - 0x12e4: 0x3000, 0x12e5: 0x3000, 0x12e6: 0x3000, 0x12e7: 0x3000, 0x12e8: 0x3000, 0x12e9: 0x3000, - 0x12ea: 0x3000, 0x12eb: 0x3000, 0x12ec: 0x3000, 0x12ed: 0x3000, 0x12ee: 0x3000, 0x12ef: 0x3000, - 0x12f0: 0x3000, 0x12f1: 0x3000, 0x12f2: 0x3000, 0x12f3: 0x3000, 0x12f4: 0x3000, 0x12f5: 0x3000, - 0x12f6: 0x3000, 0x12f7: 0x3000, 0x12f8: 0x3000, 0x12f9: 0x3000, 0x12fa: 0x3000, 0x12fb: 0x3000, - 0x12fc: 0x3000, 0x12fd: 0x3000, 0x12fe: 0x3000, 0x12ff: 0x3000, - // Block 0x4c, offset 0x1300 - 0x1300: 0x00e6, 0x1301: 0x00e6, 0x1302: 0x00dc, 0x1303: 0x00e6, 0x1304: 0x00e6, 0x1305: 0x00e6, - 0x1306: 0x00e6, 0x1307: 0x00e6, 0x1308: 0x00e6, 0x1309: 0x00e6, 0x130a: 0x00dc, 0x130b: 0x00e6, - 0x130c: 0x00e6, 0x130d: 0x00ea, 0x130e: 0x00d6, 0x130f: 0x00dc, 0x1310: 0x00ca, 0x1311: 0x00e6, - 0x1312: 0x00e6, 0x1313: 0x00e6, 0x1314: 0x00e6, 0x1315: 0x00e6, 0x1316: 0x00e6, 0x1317: 0x00e6, - 0x1318: 0x00e6, 0x1319: 0x00e6, 0x131a: 0x00e6, 0x131b: 0x00e6, 0x131c: 0x00e6, 0x131d: 0x00e6, - 0x131e: 0x00e6, 0x131f: 0x00e6, 0x1320: 0x00e6, 0x1321: 0x00e6, 0x1322: 0x00e6, 0x1323: 0x00e6, - 0x1324: 0x00e6, 0x1325: 0x00e6, 0x1326: 0x00e6, - 0x133c: 0x00e9, 0x133d: 0x00dc, 0x133e: 0x00e6, 0x133f: 0x00dc, - // Block 0x4d, offset 0x1340 - 0x1340: 0x1100, 0x1341: 0x1100, 0x1342: 0x1100, 0x1343: 0x1100, 0x1344: 0x1100, 0x1345: 0x1100, - 0x1346: 0x1100, 0x1347: 0x1100, 0x1348: 0x1100, 0x1349: 0x1100, 0x134a: 0x1100, 0x134b: 0x1100, - 0x134c: 0x1100, 0x134d: 0x1100, 0x134e: 0x1100, 0x134f: 0x1100, 0x1350: 0x1100, 0x1351: 0x1100, - 0x1352: 0x1100, 0x1353: 0x1100, 0x1354: 0x1100, 0x1355: 0x1100, 0x1356: 0x1100, 0x1357: 0x1100, - 0x1358: 0x1100, 0x1359: 0x1100, 0x135a: 0x1100, 0x135b: 0x1100, 0x135c: 0x1100, 0x135d: 0x1100, - 0x135e: 0x1100, 0x135f: 0x1100, 0x1360: 0x1100, 0x1361: 0x1100, 0x1362: 0x1100, 0x1363: 0x1100, - 0x1364: 0x1100, 0x1365: 0x1100, 0x1366: 0x1100, 0x1367: 0x1100, 0x1368: 0x1100, 0x1369: 0x1100, - 0x136a: 0x1100, 0x136b: 0x1100, 0x136c: 0x1100, 0x136d: 0x1100, 0x136e: 0x1100, 0x136f: 0x1100, - 0x1370: 0x1100, 0x1371: 0x1100, 0x1372: 0x1100, 0x1373: 0x1100, 0x1374: 0x1100, 0x1375: 0x1100, - 0x1376: 0x9900, 0x1377: 0x9900, 0x1378: 0x1100, 0x1379: 0x1100, 0x137a: 0x1100, 0x137b: 0x1100, - 0x137c: 0x1100, 0x137d: 0x1100, 0x137e: 0x1100, 0x137f: 0x1100, - // Block 0x4e, offset 0x1380 - 0x1380: 0x1100, 0x1381: 0x1100, 0x1382: 0x1100, 0x1383: 0x1100, 0x1384: 0x1100, 0x1385: 0x1100, - 0x1386: 0x1100, 0x1387: 0x1100, 0x1388: 0x1100, 0x1389: 0x1100, 0x138a: 0x1100, 0x138b: 0x1100, - 0x138c: 0x1100, 0x138d: 0x1100, 0x138e: 0x1100, 0x138f: 0x1100, 0x1390: 0x1100, 0x1391: 0x1100, - 0x1392: 0x1100, 0x1393: 0x1100, 0x1394: 0x1100, 0x1395: 0x1100, 0x1396: 0x1100, 0x1397: 0x1100, - 0x1398: 0x1100, 0x1399: 0x1100, 0x139a: 0x9900, 0x139b: 0x9900, 0x139c: 0x1100, 0x139d: 0x1100, - 0x139e: 0x1100, 0x139f: 0x1100, 0x13a0: 0x1100, 0x13a1: 0x1100, 0x13a2: 0x9900, 0x13a3: 0x9900, - 0x13a4: 0x1100, 0x13a5: 0x1100, 0x13a6: 0x1100, 0x13a7: 0x1100, 0x13a8: 0x1100, 0x13a9: 0x1100, - 0x13aa: 0x1100, 0x13ab: 0x1100, 0x13ac: 0x1100, 0x13ad: 0x1100, 0x13ae: 0x1100, 0x13af: 0x1100, - 0x13b0: 0x1100, 0x13b1: 0x1100, 0x13b2: 0x1100, 0x13b3: 0x1100, 0x13b4: 0x1100, 0x13b5: 0x1100, - 0x13b6: 0x1100, 0x13b7: 0x1100, 0x13b8: 0x1100, 0x13b9: 0x1100, 0x13ba: 0x1100, 0x13bb: 0x1100, - 0x13bc: 0x1100, 0x13bd: 0x1100, 0x13be: 0x1100, 0x13bf: 0x1100, - // Block 0x4f, offset 0x13c0 - 0x13c0: 0x1100, 0x13c1: 0x1100, 0x13c2: 0x1100, 0x13c3: 0x1100, 0x13c4: 0x1100, 0x13c5: 0x1100, - 0x13c6: 0x1100, 0x13c7: 0x1100, 0x13c8: 0x1100, 0x13c9: 0x1100, 0x13ca: 0x1100, 0x13cb: 0x1100, - 0x13cc: 0x1100, 0x13cd: 0x1100, 0x13ce: 0x1100, 0x13cf: 0x1100, 0x13d0: 0x1100, 0x13d1: 0x1100, - 0x13d2: 0x1100, 0x13d3: 0x1100, 0x13d4: 0x1100, 0x13d5: 0x1100, 0x13d6: 0x1100, 0x13d7: 0x1100, - 0x13d8: 0x1100, 0x13d9: 0x1100, 0x13da: 0x3000, 0x13db: 0x3100, - 0x13e0: 0x9900, 0x13e1: 0x9900, 0x13e2: 0x1100, 0x13e3: 0x1100, - 0x13e4: 0x1100, 0x13e5: 0x1100, 0x13e6: 0x1100, 0x13e7: 0x1100, 0x13e8: 0x1100, 0x13e9: 0x1100, - 0x13ea: 0x1100, 0x13eb: 0x1100, 0x13ec: 0x1100, 0x13ed: 0x1100, 0x13ee: 0x1100, 0x13ef: 0x1100, - 0x13f0: 0x1100, 0x13f1: 0x1100, 0x13f2: 0x1100, 0x13f3: 0x1100, 0x13f4: 0x1100, 0x13f5: 0x1100, - 0x13f6: 0x1100, 0x13f7: 0x1100, 0x13f8: 0x9900, 0x13f9: 0x9900, 0x13fa: 0x1100, 0x13fb: 0x1100, - 0x13fc: 0x1100, 0x13fd: 0x1100, 0x13fe: 0x1100, 0x13ff: 0x1100, - // Block 0x50, offset 0x1400 - 0x1400: 0x1100, 0x1401: 0x1100, 0x1402: 0x1100, 0x1403: 0x1100, 0x1404: 0x1100, 0x1405: 0x1100, - 0x1406: 0x1100, 0x1407: 0x1100, 0x1408: 0x1100, 0x1409: 0x1100, 0x140a: 0x1100, 0x140b: 0x1100, - 0x140c: 0x9900, 0x140d: 0x9900, 0x140e: 0x1100, 0x140f: 0x1100, 0x1410: 0x1100, 0x1411: 0x1100, - 0x1412: 0x1100, 0x1413: 0x1100, 0x1414: 0x1100, 0x1415: 0x1100, 0x1416: 0x1100, 0x1417: 0x1100, - 0x1418: 0x1100, 0x1419: 0x1100, 0x141a: 0x1100, 0x141b: 0x1100, 0x141c: 0x1100, 0x141d: 0x1100, - 0x141e: 0x1100, 0x141f: 0x1100, 0x1420: 0x1100, 0x1421: 0x1100, 0x1422: 0x1100, 0x1423: 0x1100, - 0x1424: 0x1100, 0x1425: 0x1100, 0x1426: 0x1100, 0x1427: 0x1100, 0x1428: 0x1100, 0x1429: 0x1100, - 0x142a: 0x1100, 0x142b: 0x1100, 0x142c: 0x1100, 0x142d: 0x1100, 0x142e: 0x1100, 0x142f: 0x1100, - 0x1430: 0x1100, 0x1431: 0x1100, 0x1432: 0x1100, 0x1433: 0x1100, 0x1434: 0x1100, 0x1435: 0x1100, - 0x1436: 0x1100, 0x1437: 0x1100, 0x1438: 0x1100, 0x1439: 0x1100, - // Block 0x51, offset 0x1440 - 0x1440: 0x9900, 0x1441: 0x9900, 0x1442: 0x9900, 0x1443: 0x9900, 0x1444: 0x9900, 0x1445: 0x9900, - 0x1446: 0x9900, 0x1447: 0x9900, 0x1448: 0x9900, 0x1449: 0x9900, 0x144a: 0x9900, 0x144b: 0x9900, - 0x144c: 0x9900, 0x144d: 0x9900, 0x144e: 0x9900, 0x144f: 0x9900, 0x1450: 0x9900, 0x1451: 0x9900, - 0x1452: 0x1100, 0x1453: 0x1100, 0x1454: 0x1100, 0x1455: 0x1100, - 0x1458: 0x9900, 0x1459: 0x9900, 0x145a: 0x1100, 0x145b: 0x1100, 0x145c: 0x1100, 0x145d: 0x1100, - 0x1460: 0x9900, 0x1461: 0x9900, 0x1462: 0x9900, 0x1463: 0x9900, - 0x1464: 0x9900, 0x1465: 0x9900, 0x1466: 0x9900, 0x1467: 0x9900, 0x1468: 0x9900, 0x1469: 0x9900, - 0x146a: 0x9900, 0x146b: 0x9900, 0x146c: 0x9900, 0x146d: 0x9900, 0x146e: 0x9900, 0x146f: 0x9900, - 0x1470: 0x9900, 0x1471: 0x9900, 0x1472: 0x1100, 0x1473: 0x1100, 0x1474: 0x1100, 0x1475: 0x1100, - 0x1476: 0x1100, 0x1477: 0x1100, 0x1478: 0x9900, 0x1479: 0x9900, 0x147a: 0x1100, 0x147b: 0x1100, - 0x147c: 0x1100, 0x147d: 0x1100, 0x147e: 0x1100, 0x147f: 0x1100, - // Block 0x52, offset 0x1480 - 0x1480: 0x9900, 0x1481: 0x9900, 0x1482: 0x1100, 0x1483: 0x1100, 0x1484: 0x1100, 0x1485: 0x1100, - 0x1488: 0x9900, 0x1489: 0x9900, 0x148a: 0x1100, 0x148b: 0x1100, - 0x148c: 0x1100, 0x148d: 0x1100, 0x1490: 0x9900, 0x1491: 0x9900, - 0x1492: 0x1100, 0x1493: 0x1100, 0x1494: 0x1100, 0x1495: 0x1100, 0x1496: 0x1100, 0x1497: 0x1100, - 0x1499: 0x9900, 0x149b: 0x1100, 0x149d: 0x1100, - 0x149f: 0x1100, 0x14a0: 0x9900, 0x14a1: 0x9900, 0x14a2: 0x9900, 0x14a3: 0x9900, - 0x14a4: 0x9900, 0x14a5: 0x9900, 0x14a6: 0x9900, 0x14a7: 0x9900, 0x14a8: 0x9900, 0x14a9: 0x9900, - 0x14aa: 0x9900, 0x14ab: 0x9900, 0x14ac: 0x9900, 0x14ad: 0x9900, 0x14ae: 0x9900, 0x14af: 0x9900, - 0x14b0: 0x9900, 0x14b1: 0x3300, 0x14b2: 0x1100, 0x14b3: 0x3300, 0x14b4: 0x9900, 0x14b5: 0x3300, - 0x14b6: 0x1100, 0x14b7: 0x3300, 0x14b8: 0x1100, 0x14b9: 0x3300, 0x14ba: 0x1100, 0x14bb: 0x3300, - 0x14bc: 0x9900, 0x14bd: 0x3300, - // Block 0x53, offset 0x14c0 - 0x14c0: 0x1100, 0x14c1: 0x1100, 0x14c2: 0x1100, 0x14c3: 0x1100, 0x14c4: 0x1100, 0x14c5: 0x1100, - 0x14c6: 0x1100, 0x14c7: 0x1100, 0x14c8: 0x1100, 0x14c9: 0x1100, 0x14ca: 0x1100, 0x14cb: 0x1100, - 0x14cc: 0x1100, 0x14cd: 0x1100, 0x14ce: 0x1100, 0x14cf: 0x1100, 0x14d0: 0x1100, 0x14d1: 0x1100, - 0x14d2: 0x1100, 0x14d3: 0x1100, 0x14d4: 0x1100, 0x14d5: 0x1100, 0x14d6: 0x1100, 0x14d7: 0x1100, - 0x14d8: 0x1100, 0x14d9: 0x1100, 0x14da: 0x1100, 0x14db: 0x1100, 0x14dc: 0x1100, 0x14dd: 0x1100, - 0x14de: 0x1100, 0x14df: 0x1100, 0x14e0: 0x1100, 0x14e1: 0x1100, 0x14e2: 0x1100, 0x14e3: 0x1100, - 0x14e4: 0x1100, 0x14e5: 0x1100, 0x14e6: 0x1100, 0x14e7: 0x1100, 0x14e8: 0x1100, 0x14e9: 0x1100, - 0x14ea: 0x1100, 0x14eb: 0x1100, 0x14ec: 0x1100, 0x14ed: 0x1100, 0x14ee: 0x1100, 0x14ef: 0x1100, - 0x14f0: 0x1100, 0x14f1: 0x1100, 0x14f2: 0x1100, 0x14f3: 0x1100, 0x14f4: 0x1100, - 0x14f6: 0x9900, 0x14f7: 0x1100, 0x14f8: 0x1100, 0x14f9: 0x1100, 0x14fa: 0x1100, 0x14fb: 0x3300, - 0x14fc: 0x1100, 0x14fd: 0x3000, 0x14fe: 0x3300, 0x14ff: 0x3800, - // Block 0x54, offset 0x1500 - 0x1500: 0x3000, 0x1501: 0x3100, 0x1502: 0x1100, 0x1503: 0x1100, 0x1504: 0x1100, - 0x1506: 0x9900, 0x1507: 0x1100, 0x1508: 0x1100, 0x1509: 0x3300, 0x150a: 0x1100, 0x150b: 0x3300, - 0x150c: 0x1100, 0x150d: 0x3100, 0x150e: 0x3100, 0x150f: 0x3100, 0x1510: 0x1100, 0x1511: 0x1100, - 0x1512: 0x1100, 0x1513: 0x3300, 0x1516: 0x1100, 0x1517: 0x1100, - 0x1518: 0x1100, 0x1519: 0x1100, 0x151a: 0x1100, 0x151b: 0x3300, 0x151d: 0x3100, - 0x151e: 0x3100, 0x151f: 0x3100, 0x1520: 0x1100, 0x1521: 0x1100, 0x1522: 0x1100, 0x1523: 0x3300, - 0x1524: 0x1100, 0x1525: 0x1100, 0x1526: 0x1100, 0x1527: 0x1100, 0x1528: 0x1100, 0x1529: 0x1100, - 0x152a: 0x1100, 0x152b: 0x3300, 0x152c: 0x1100, 0x152d: 0x3100, 0x152e: 0x3300, 0x152f: 0x3300, - 0x1532: 0x1100, 0x1533: 0x1100, 0x1534: 0x1100, - 0x1536: 0x9900, 0x1537: 0x1100, 0x1538: 0x1100, 0x1539: 0x3300, 0x153a: 0x1100, 0x153b: 0x3300, - 0x153c: 0x1100, 0x153d: 0x3300, 0x153e: 0x3800, - // Block 0x55, offset 0x1540 - 0x1540: 0x3300, 0x1541: 0x3300, 0x1542: 0x3000, 0x1543: 0x3000, 0x1544: 0x3000, 0x1545: 0x3000, - 0x1546: 0x3000, 0x1547: 0x3000, 0x1548: 0x3000, 0x1549: 0x3000, 0x154a: 0x3000, - 0x1551: 0x3000, - 0x1557: 0x3000, - 0x1564: 0x3000, 0x1565: 0x3000, 0x1566: 0x3000, - 0x156f: 0x3000, - 0x1573: 0x3000, 0x1574: 0x3000, - 0x1576: 0x3000, 0x1577: 0x3000, - 0x157c: 0x3000, 0x157e: 0x3000, - // Block 0x56, offset 0x1580 - 0x1587: 0x3000, 0x1588: 0x3000, 0x1589: 0x3000, - 0x1597: 0x3000, - 0x159f: 0x3000, - 0x15b0: 0x3000, 0x15b1: 0x3000, 0x15b4: 0x3000, 0x15b5: 0x3000, - 0x15b6: 0x3000, 0x15b7: 0x3000, 0x15b8: 0x3000, 0x15b9: 0x3000, 0x15ba: 0x3000, 0x15bb: 0x3000, - 0x15bc: 0x3000, 0x15bd: 0x3000, 0x15be: 0x3000, 0x15bf: 0x3000, - // Block 0x57, offset 0x15c0 - 0x15c0: 0x3000, 0x15c1: 0x3000, 0x15c2: 0x3000, 0x15c3: 0x3000, 0x15c4: 0x3000, 0x15c5: 0x3000, - 0x15c6: 0x3000, 0x15c7: 0x3000, 0x15c8: 0x3000, 0x15c9: 0x3000, 0x15ca: 0x3000, 0x15cb: 0x3000, - 0x15cc: 0x3000, 0x15cd: 0x3000, 0x15ce: 0x3000, 0x15d0: 0x3000, 0x15d1: 0x3000, - 0x15d2: 0x3000, 0x15d3: 0x3000, 0x15d4: 0x3000, 0x15d5: 0x3000, 0x15d6: 0x3000, 0x15d7: 0x3000, - 0x15d8: 0x3000, 0x15d9: 0x3000, 0x15da: 0x3000, 0x15db: 0x3000, 0x15dc: 0x3000, - 0x15e8: 0x3000, - // Block 0x58, offset 0x1600 - 0x1610: 0x00e6, 0x1611: 0x00e6, - 0x1612: 0x0001, 0x1613: 0x0001, 0x1614: 0x00e6, 0x1615: 0x00e6, 0x1616: 0x00e6, 0x1617: 0x00e6, - 0x1618: 0x0001, 0x1619: 0x0001, 0x161a: 0x0001, 0x161b: 0x00e6, 0x161c: 0x00e6, - 0x1621: 0x00e6, - 0x1625: 0x0001, 0x1626: 0x0001, 0x1627: 0x00e6, 0x1628: 0x00dc, 0x1629: 0x00e6, - 0x162a: 0x0001, 0x162b: 0x0001, 0x162c: 0x00dc, 0x162d: 0x00dc, 0x162e: 0x00dc, 0x162f: 0x00dc, - 0x1630: 0x00e6, - // Block 0x59, offset 0x1640 - 0x1640: 0x3000, 0x1641: 0x3000, 0x1642: 0x3000, 0x1643: 0x3000, 0x1645: 0x3000, - 0x1646: 0x3000, 0x1647: 0x3000, 0x1649: 0x3000, 0x164a: 0x3000, 0x164b: 0x3000, - 0x164c: 0x3000, 0x164d: 0x3000, 0x164e: 0x3000, 0x164f: 0x3000, 0x1650: 0x3000, 0x1651: 0x3000, - 0x1652: 0x3000, 0x1653: 0x3000, 0x1655: 0x3000, 0x1656: 0x3000, - 0x1659: 0x3000, 0x165a: 0x3000, 0x165b: 0x3000, 0x165c: 0x3000, 0x165d: 0x3000, - 0x1660: 0x3000, 0x1661: 0x3000, 0x1662: 0x3000, - 0x1664: 0x3000, 0x1666: 0x3300, 0x1668: 0x3000, - 0x166a: 0x3300, 0x166b: 0x3300, 0x166c: 0x3000, 0x166d: 0x3000, 0x166f: 0x3000, - 0x1670: 0x3000, 0x1671: 0x3000, 0x1673: 0x3000, 0x1674: 0x3000, 0x1675: 0x3000, - 0x1676: 0x3000, 0x1677: 0x3000, 0x1678: 0x3000, 0x1679: 0x3000, 0x167b: 0x3000, - 0x167c: 0x3000, 0x167d: 0x3000, 0x167e: 0x3000, 0x167f: 0x3000, - // Block 0x5a, offset 0x1680 - 0x1680: 0x3000, 0x1685: 0x3000, - 0x1686: 0x3000, 0x1687: 0x3000, 0x1688: 0x3000, 0x1689: 0x3000, - 0x1690: 0x3000, 0x1691: 0x3000, - 0x1692: 0x3000, 0x1693: 0x3000, 0x1694: 0x3000, 0x1695: 0x3000, 0x1696: 0x3000, 0x1697: 0x3000, - 0x1698: 0x3000, 0x1699: 0x3000, 0x169a: 0x3000, 0x169b: 0x3000, 0x169c: 0x3000, 0x169d: 0x3000, - 0x169e: 0x3000, 0x169f: 0x3000, 0x16a0: 0x3000, 0x16a1: 0x3000, 0x16a2: 0x3000, 0x16a3: 0x3000, - 0x16a4: 0x3000, 0x16a5: 0x3000, 0x16a6: 0x3000, 0x16a7: 0x3000, 0x16a8: 0x3000, 0x16a9: 0x3000, - 0x16aa: 0x3000, 0x16ab: 0x3000, 0x16ac: 0x3000, 0x16ad: 0x3000, 0x16ae: 0x3000, 0x16af: 0x3000, - 0x16b0: 0x3000, 0x16b1: 0x3000, 0x16b2: 0x3000, 0x16b3: 0x3000, 0x16b4: 0x3000, 0x16b5: 0x3000, - 0x16b6: 0x3000, 0x16b7: 0x3000, 0x16b8: 0x3000, 0x16b9: 0x3000, 0x16ba: 0x3000, 0x16bb: 0x3000, - 0x16bc: 0x3000, 0x16bd: 0x3000, 0x16be: 0x3000, 0x16bf: 0x3000, - // Block 0x5b, offset 0x16c0 - 0x16c9: 0x3000, - 0x16d0: 0x8800, - 0x16d2: 0x8800, 0x16d4: 0x8800, - 0x16da: 0x1100, 0x16db: 0x1100, - 0x16ee: 0x1100, - // Block 0x5c, offset 0x1700 - 0x170d: 0x1100, 0x170e: 0x1100, 0x170f: 0x1100, 0x1710: 0x8800, - 0x1712: 0x8800, 0x1714: 0x8800, - // Block 0x5d, offset 0x1740 - 0x1743: 0x8800, 0x1744: 0x1100, - 0x1748: 0x8800, 0x1749: 0x1100, 0x174b: 0x8800, - 0x174c: 0x1100, - 0x1763: 0x8800, - 0x1764: 0x1100, 0x1765: 0x8800, 0x1766: 0x1100, - 0x176c: 0x3000, 0x176d: 0x3000, 0x176f: 0x3000, - 0x1770: 0x3000, - 0x177c: 0x8800, - // Block 0x5e, offset 0x1780 - 0x1781: 0x1100, 0x1783: 0x8800, 0x1784: 0x1100, 0x1785: 0x8800, - 0x1787: 0x1100, 0x1788: 0x8800, 0x1789: 0x1100, - 0x178d: 0x8800, - 0x17a0: 0x1100, 0x17a1: 0x8800, 0x17a2: 0x1100, - 0x17a4: 0x8800, 0x17a5: 0x8800, - 0x17ad: 0x1100, 0x17ae: 0x1100, 0x17af: 0x1100, - 0x17b0: 0x1100, 0x17b1: 0x1100, 0x17b2: 0x8800, 0x17b3: 0x8800, 0x17b4: 0x1100, 0x17b5: 0x1100, - 0x17b6: 0x8800, 0x17b7: 0x8800, 0x17b8: 0x1100, 0x17b9: 0x1100, 0x17ba: 0x8800, 0x17bb: 0x8800, - 0x17bc: 0x8800, 0x17bd: 0x8800, - // Block 0x5f, offset 0x17c0 - 0x17c0: 0x1100, 0x17c1: 0x1100, 0x17c2: 0x8800, 0x17c3: 0x8800, 0x17c4: 0x1100, 0x17c5: 0x1100, - 0x17c6: 0x8800, 0x17c7: 0x8800, 0x17c8: 0x1100, 0x17c9: 0x1100, - 0x17d1: 0x8800, - 0x17d2: 0x8800, - 0x17e2: 0x8800, - 0x17e8: 0x8800, 0x17e9: 0x8800, - 0x17eb: 0x8800, 0x17ec: 0x1100, 0x17ed: 0x1100, 0x17ee: 0x1100, 0x17ef: 0x1100, - 0x17f2: 0x8800, 0x17f3: 0x8800, 0x17f4: 0x8800, 0x17f5: 0x8800, - // Block 0x60, offset 0x1800 - 0x1820: 0x1100, 0x1821: 0x1100, 0x1822: 0x1100, 0x1823: 0x1100, - 0x182a: 0x1100, 0x182b: 0x1100, 0x182c: 0x1100, 0x182d: 0x1100, - // Block 0x61, offset 0x1840 - 0x1869: 0x3300, - 0x186a: 0x3300, - // Block 0x62, offset 0x1880 - 0x18a0: 0x3000, 0x18a1: 0x3000, 0x18a2: 0x3000, 0x18a3: 0x3000, - 0x18a4: 0x3000, 0x18a5: 0x3000, 0x18a6: 0x3000, 0x18a7: 0x3000, 0x18a8: 0x3000, 0x18a9: 0x3000, - 0x18aa: 0x3000, 0x18ab: 0x3000, 0x18ac: 0x3000, 0x18ad: 0x3000, 0x18ae: 0x3000, 0x18af: 0x3000, - 0x18b0: 0x3000, 0x18b1: 0x3000, 0x18b2: 0x3000, 0x18b3: 0x3000, 0x18b4: 0x3000, 0x18b5: 0x3000, - 0x18b6: 0x3000, 0x18b7: 0x3000, 0x18b8: 0x3000, 0x18b9: 0x3000, 0x18ba: 0x3000, 0x18bb: 0x3000, - 0x18bc: 0x3000, 0x18bd: 0x3000, 0x18be: 0x3000, 0x18bf: 0x3000, - // Block 0x63, offset 0x18c0 - 0x18c0: 0x3000, 0x18c1: 0x3000, 0x18c2: 0x3000, 0x18c3: 0x3000, 0x18c4: 0x3000, 0x18c5: 0x3000, - 0x18c6: 0x3000, 0x18c7: 0x3000, 0x18c8: 0x3000, 0x18c9: 0x3000, 0x18ca: 0x3000, 0x18cb: 0x3000, - 0x18cc: 0x3000, 0x18cd: 0x3000, 0x18ce: 0x3000, 0x18cf: 0x3000, 0x18d0: 0x3000, 0x18d1: 0x3000, - 0x18d2: 0x3000, 0x18d3: 0x3000, 0x18d4: 0x3000, 0x18d5: 0x3000, 0x18d6: 0x3000, 0x18d7: 0x3000, - 0x18d8: 0x3000, 0x18d9: 0x3000, 0x18da: 0x3000, 0x18db: 0x3000, 0x18dc: 0x3000, 0x18dd: 0x3000, - 0x18de: 0x3000, 0x18df: 0x3000, 0x18e0: 0x3000, 0x18e1: 0x3000, 0x18e2: 0x3000, 0x18e3: 0x3000, - 0x18e4: 0x3000, 0x18e5: 0x3000, 0x18e6: 0x3000, 0x18e7: 0x3000, 0x18e8: 0x3000, 0x18e9: 0x3000, - 0x18ea: 0x3000, 0x18eb: 0x3000, 0x18ec: 0x3000, 0x18ed: 0x3000, 0x18ee: 0x3000, 0x18ef: 0x3000, - 0x18f0: 0x3000, 0x18f1: 0x3000, 0x18f2: 0x3000, 0x18f3: 0x3000, 0x18f4: 0x3000, 0x18f5: 0x3000, - 0x18f6: 0x3000, 0x18f7: 0x3000, 0x18f8: 0x3000, 0x18f9: 0x3000, 0x18fa: 0x3000, 0x18fb: 0x3000, - 0x18fc: 0x3000, 0x18fd: 0x3000, 0x18fe: 0x3000, 0x18ff: 0x3000, - // Block 0x64, offset 0x1900 - 0x1900: 0x3000, 0x1901: 0x3000, 0x1902: 0x3000, 0x1903: 0x3000, 0x1904: 0x3000, 0x1905: 0x3000, - 0x1906: 0x3000, 0x1907: 0x3000, 0x1908: 0x3000, 0x1909: 0x3000, 0x190a: 0x3000, 0x190b: 0x3000, - 0x190c: 0x3000, 0x190d: 0x3000, 0x190e: 0x3000, 0x190f: 0x3000, 0x1910: 0x3000, 0x1911: 0x3000, - 0x1912: 0x3000, 0x1913: 0x3000, 0x1914: 0x3000, 0x1915: 0x3000, 0x1916: 0x3000, 0x1917: 0x3000, - 0x1918: 0x3000, 0x1919: 0x3000, 0x191a: 0x3000, 0x191b: 0x3000, 0x191c: 0x3000, 0x191d: 0x3000, - 0x191e: 0x3000, 0x191f: 0x3000, 0x1920: 0x3000, 0x1921: 0x3000, 0x1922: 0x3000, 0x1923: 0x3000, - 0x1924: 0x3000, 0x1925: 0x3000, 0x1926: 0x3000, 0x1927: 0x3000, 0x1928: 0x3000, 0x1929: 0x3000, - 0x192a: 0x3000, - // Block 0x65, offset 0x1940 - 0x194c: 0x3000, - // Block 0x66, offset 0x1980 - 0x19b4: 0x3000, 0x19b5: 0x3000, - 0x19b6: 0x3000, - // Block 0x67, offset 0x19c0 - 0x19dc: 0x3300, - // Block 0x68, offset 0x1a00 - 0x1a3c: 0x3000, 0x1a3d: 0x3000, - // Block 0x69, offset 0x1a40 - 0x1a6f: 0x00e6, - 0x1a70: 0x00e6, 0x1a71: 0x00e6, - // Block 0x6a, offset 0x1a80 - 0x1aaf: 0x3000, - 0x1abf: 0x0009, - // Block 0x6b, offset 0x1ac0 - 0x1ae0: 0x00e6, 0x1ae1: 0x00e6, 0x1ae2: 0x00e6, 0x1ae3: 0x00e6, - 0x1ae4: 0x00e6, 0x1ae5: 0x00e6, 0x1ae6: 0x00e6, 0x1ae7: 0x00e6, 0x1ae8: 0x00e6, 0x1ae9: 0x00e6, - 0x1aea: 0x00e6, 0x1aeb: 0x00e6, 0x1aec: 0x00e6, 0x1aed: 0x00e6, 0x1aee: 0x00e6, 0x1aef: 0x00e6, - 0x1af0: 0x00e6, 0x1af1: 0x00e6, 0x1af2: 0x00e6, 0x1af3: 0x00e6, 0x1af4: 0x00e6, 0x1af5: 0x00e6, - 0x1af6: 0x00e6, 0x1af7: 0x00e6, 0x1af8: 0x00e6, 0x1af9: 0x00e6, 0x1afa: 0x00e6, 0x1afb: 0x00e6, - 0x1afc: 0x00e6, 0x1afd: 0x00e6, 0x1afe: 0x00e6, 0x1aff: 0x00e6, - // Block 0x6c, offset 0x1b00 - 0x1b1f: 0x3000, - // Block 0x6d, offset 0x1b40 - 0x1b73: 0x3000, - // Block 0x6e, offset 0x1b80 - 0x1b80: 0x3000, 0x1b81: 0x3000, 0x1b82: 0x3000, 0x1b83: 0x3000, 0x1b84: 0x3000, 0x1b85: 0x3000, - 0x1b86: 0x3000, 0x1b87: 0x3000, 0x1b88: 0x3000, 0x1b89: 0x3000, 0x1b8a: 0x3000, 0x1b8b: 0x3000, - 0x1b8c: 0x3000, 0x1b8d: 0x3000, 0x1b8e: 0x3000, 0x1b8f: 0x3000, 0x1b90: 0x3000, 0x1b91: 0x3000, - 0x1b92: 0x3000, 0x1b93: 0x3000, 0x1b94: 0x3000, 0x1b95: 0x3000, - // Block 0x6f, offset 0x1bc0 - 0x1bc0: 0x3000, - 0x1bea: 0x00da, 0x1beb: 0x00e4, 0x1bec: 0x00e8, 0x1bed: 0x00de, 0x1bee: 0x00e0, 0x1bef: 0x00e0, - 0x1bf6: 0x3000, 0x1bf8: 0x3000, 0x1bf9: 0x3000, 0x1bfa: 0x3000, - // Block 0x70, offset 0x1c00 - 0x1c06: 0x8800, 0x1c0b: 0x8800, - 0x1c0c: 0x1100, 0x1c0d: 0x8800, 0x1c0e: 0x1100, 0x1c0f: 0x8800, 0x1c10: 0x1100, 0x1c11: 0x8800, - 0x1c12: 0x1100, 0x1c13: 0x8800, 0x1c14: 0x1100, 0x1c15: 0x8800, 0x1c16: 0x1100, 0x1c17: 0x8800, - 0x1c18: 0x1100, 0x1c19: 0x8800, 0x1c1a: 0x1100, 0x1c1b: 0x8800, 0x1c1c: 0x1100, 0x1c1d: 0x8800, - 0x1c1e: 0x1100, 0x1c1f: 0x8800, 0x1c20: 0x1100, 0x1c21: 0x8800, 0x1c22: 0x1100, - 0x1c24: 0x8800, 0x1c25: 0x1100, 0x1c26: 0x8800, 0x1c27: 0x1100, 0x1c28: 0x8800, 0x1c29: 0x1100, - 0x1c2f: 0x8800, - 0x1c30: 0x1100, 0x1c31: 0x1100, 0x1c32: 0x8800, 0x1c33: 0x1100, 0x1c34: 0x1100, 0x1c35: 0x8800, - 0x1c36: 0x1100, 0x1c37: 0x1100, 0x1c38: 0x8800, 0x1c39: 0x1100, 0x1c3a: 0x1100, 0x1c3b: 0x8800, - 0x1c3c: 0x1100, 0x1c3d: 0x1100, - // Block 0x71, offset 0x1c40 - 0x1c54: 0x1100, - 0x1c59: 0x6608, 0x1c5a: 0x6608, 0x1c5b: 0x3000, 0x1c5c: 0x3000, 0x1c5d: 0x8800, - 0x1c5e: 0x1100, 0x1c5f: 0x3000, - 0x1c66: 0x8800, - 0x1c6b: 0x8800, 0x1c6c: 0x1100, 0x1c6d: 0x8800, 0x1c6e: 0x1100, 0x1c6f: 0x8800, - 0x1c70: 0x1100, 0x1c71: 0x8800, 0x1c72: 0x1100, 0x1c73: 0x8800, 0x1c74: 0x1100, 0x1c75: 0x8800, - 0x1c76: 0x1100, 0x1c77: 0x8800, 0x1c78: 0x1100, 0x1c79: 0x8800, 0x1c7a: 0x1100, 0x1c7b: 0x8800, - 0x1c7c: 0x1100, 0x1c7d: 0x8800, 0x1c7e: 0x1100, 0x1c7f: 0x8800, - // Block 0x72, offset 0x1c80 - 0x1c80: 0x1100, 0x1c81: 0x8800, 0x1c82: 0x1100, 0x1c84: 0x8800, 0x1c85: 0x1100, - 0x1c86: 0x8800, 0x1c87: 0x1100, 0x1c88: 0x8800, 0x1c89: 0x1100, - 0x1c8f: 0x8800, 0x1c90: 0x1100, 0x1c91: 0x1100, - 0x1c92: 0x8800, 0x1c93: 0x1100, 0x1c94: 0x1100, 0x1c95: 0x8800, 0x1c96: 0x1100, 0x1c97: 0x1100, - 0x1c98: 0x8800, 0x1c99: 0x1100, 0x1c9a: 0x1100, 0x1c9b: 0x8800, 0x1c9c: 0x1100, 0x1c9d: 0x1100, - 0x1caf: 0x8800, - 0x1cb0: 0x8800, 0x1cb1: 0x8800, 0x1cb2: 0x8800, 0x1cb4: 0x1100, - 0x1cb7: 0x1100, 0x1cb8: 0x1100, 0x1cb9: 0x1100, 0x1cba: 0x1100, - 0x1cbd: 0x8800, 0x1cbe: 0x1100, 0x1cbf: 0x3000, - // Block 0x73, offset 0x1cc0 - 0x1cf1: 0x3000, 0x1cf2: 0x3000, 0x1cf3: 0x3000, 0x1cf4: 0x3000, 0x1cf5: 0x3000, - 0x1cf6: 0x3000, 0x1cf7: 0x3000, 0x1cf8: 0x3000, 0x1cf9: 0x3000, 0x1cfa: 0x3000, 0x1cfb: 0x3000, - 0x1cfc: 0x3000, 0x1cfd: 0x3000, 0x1cfe: 0x3000, 0x1cff: 0x3000, - // Block 0x74, offset 0x1d00 - 0x1d00: 0x3000, 0x1d01: 0x3000, 0x1d02: 0x3000, 0x1d03: 0x3000, 0x1d04: 0x3000, 0x1d05: 0x3000, - 0x1d06: 0x3000, 0x1d07: 0x3000, 0x1d08: 0x3000, 0x1d09: 0x3000, 0x1d0a: 0x3000, 0x1d0b: 0x3000, - 0x1d0c: 0x3000, 0x1d0d: 0x3000, 0x1d0e: 0x3000, - 0x1d12: 0x3000, 0x1d13: 0x3000, 0x1d14: 0x3000, 0x1d15: 0x3000, 0x1d16: 0x3000, 0x1d17: 0x3000, - 0x1d18: 0x3000, 0x1d19: 0x3000, 0x1d1a: 0x3000, 0x1d1b: 0x3000, 0x1d1c: 0x3000, 0x1d1d: 0x3000, - 0x1d1e: 0x3000, 0x1d1f: 0x3000, - // Block 0x75, offset 0x1d40 - 0x1d40: 0x3000, 0x1d41: 0x3000, 0x1d42: 0x3000, 0x1d43: 0x3000, 0x1d44: 0x3000, 0x1d45: 0x3000, - 0x1d46: 0x3000, 0x1d47: 0x3000, 0x1d48: 0x3000, 0x1d49: 0x3000, 0x1d4a: 0x3000, 0x1d4b: 0x3000, - 0x1d4c: 0x3000, 0x1d4d: 0x3000, 0x1d4e: 0x3000, 0x1d4f: 0x3000, 0x1d50: 0x3000, 0x1d51: 0x3000, - 0x1d52: 0x3000, 0x1d53: 0x3000, 0x1d54: 0x3000, 0x1d55: 0x3000, 0x1d56: 0x3000, 0x1d57: 0x3000, - 0x1d58: 0x3000, 0x1d59: 0x3000, 0x1d5a: 0x3000, 0x1d5b: 0x3000, 0x1d5c: 0x3000, 0x1d5d: 0x3000, - 0x1d5e: 0x3000, 0x1d60: 0x3000, 0x1d61: 0x3000, 0x1d62: 0x3000, 0x1d63: 0x3000, - 0x1d64: 0x3000, 0x1d65: 0x3000, 0x1d66: 0x3000, 0x1d67: 0x3000, 0x1d68: 0x3000, 0x1d69: 0x3000, - 0x1d6a: 0x3000, 0x1d6b: 0x3000, 0x1d6c: 0x3000, 0x1d6d: 0x3000, 0x1d6e: 0x3000, 0x1d6f: 0x3000, - 0x1d70: 0x3000, 0x1d71: 0x3000, 0x1d72: 0x3000, 0x1d73: 0x3000, 0x1d74: 0x3000, 0x1d75: 0x3000, - 0x1d76: 0x3000, 0x1d77: 0x3000, 0x1d78: 0x3000, 0x1d79: 0x3000, 0x1d7a: 0x3000, 0x1d7b: 0x3000, - 0x1d7c: 0x3000, 0x1d7d: 0x3000, 0x1d7e: 0x3000, 0x1d7f: 0x3000, - // Block 0x76, offset 0x1d80 - 0x1d80: 0x3000, 0x1d81: 0x3000, 0x1d82: 0x3000, 0x1d83: 0x3000, 0x1d84: 0x3000, 0x1d85: 0x3000, - 0x1d86: 0x3000, 0x1d87: 0x3000, - 0x1d90: 0x3000, 0x1d91: 0x3000, - 0x1d92: 0x3000, 0x1d93: 0x3000, 0x1d94: 0x3000, 0x1d95: 0x3000, 0x1d96: 0x3000, 0x1d97: 0x3000, - 0x1d98: 0x3000, 0x1d99: 0x3000, 0x1d9a: 0x3000, 0x1d9b: 0x3000, 0x1d9c: 0x3000, 0x1d9d: 0x3000, - 0x1d9e: 0x3000, 0x1d9f: 0x3000, 0x1da0: 0x3000, 0x1da1: 0x3000, 0x1da2: 0x3000, 0x1da3: 0x3000, - 0x1da4: 0x3000, 0x1da5: 0x3000, 0x1da6: 0x3000, 0x1da7: 0x3000, 0x1da8: 0x3000, 0x1da9: 0x3000, - 0x1daa: 0x3000, 0x1dab: 0x3000, 0x1dac: 0x3000, 0x1dad: 0x3000, 0x1dae: 0x3000, 0x1daf: 0x3000, - 0x1db0: 0x3000, 0x1db1: 0x3000, 0x1db2: 0x3000, 0x1db3: 0x3000, 0x1db4: 0x3000, 0x1db5: 0x3000, - 0x1db6: 0x3000, 0x1db7: 0x3000, 0x1db8: 0x3000, 0x1db9: 0x3000, 0x1dba: 0x3000, 0x1dbb: 0x3000, - 0x1dbc: 0x3000, 0x1dbd: 0x3000, 0x1dbe: 0x3000, - // Block 0x77, offset 0x1dc0 - 0x1dc0: 0x3000, 0x1dc1: 0x3000, 0x1dc2: 0x3000, 0x1dc3: 0x3000, 0x1dc4: 0x3000, 0x1dc5: 0x3000, - 0x1dc6: 0x3000, 0x1dc7: 0x3000, 0x1dc8: 0x3000, 0x1dc9: 0x3000, 0x1dca: 0x3000, 0x1dcb: 0x3000, - 0x1dcc: 0x3000, 0x1dcd: 0x3000, 0x1dce: 0x3000, 0x1dcf: 0x3000, 0x1dd0: 0x3000, 0x1dd1: 0x3000, - 0x1dd2: 0x3000, 0x1dd3: 0x3000, 0x1dd4: 0x3000, 0x1dd5: 0x3000, 0x1dd6: 0x3000, 0x1dd7: 0x3000, - 0x1dd8: 0x3000, 0x1dd9: 0x3000, 0x1dda: 0x3000, 0x1ddb: 0x3000, 0x1ddc: 0x3000, 0x1ddd: 0x3000, - 0x1dde: 0x3000, 0x1ddf: 0x3000, 0x1de0: 0x3000, 0x1de1: 0x3000, 0x1de2: 0x3000, 0x1de3: 0x3000, - 0x1de4: 0x3000, 0x1de5: 0x3000, 0x1de6: 0x3000, 0x1de7: 0x3000, 0x1de8: 0x3000, 0x1de9: 0x3000, - 0x1dea: 0x3000, 0x1deb: 0x3000, 0x1dec: 0x3000, 0x1ded: 0x3000, 0x1dee: 0x3000, 0x1def: 0x3000, - 0x1df0: 0x3000, 0x1df1: 0x3000, 0x1df2: 0x3000, 0x1df3: 0x3000, 0x1df4: 0x3000, 0x1df5: 0x3000, - 0x1df6: 0x3000, 0x1df7: 0x3000, 0x1df8: 0x3000, 0x1df9: 0x3000, 0x1dfa: 0x3000, 0x1dfb: 0x3000, - 0x1dfc: 0x3000, 0x1dfd: 0x3000, 0x1dfe: 0x3000, - // Block 0x78, offset 0x1e00 - 0x1e2f: 0x00e6, - 0x1e3c: 0x00e6, 0x1e3d: 0x00e6, - // Block 0x79, offset 0x1e40 - 0x1e70: 0x00e6, 0x1e71: 0x00e6, - // Block 0x7a, offset 0x1e80 - 0x1eb0: 0x3000, - // Block 0x7b, offset 0x1ec0 - 0x1ec6: 0x0009, - // Block 0x7c, offset 0x1f00 - 0x1f04: 0x0009, - 0x1f20: 0x00e6, 0x1f21: 0x00e6, 0x1f22: 0x00e6, 0x1f23: 0x00e6, - 0x1f24: 0x00e6, 0x1f25: 0x00e6, 0x1f26: 0x00e6, 0x1f27: 0x00e6, 0x1f28: 0x00e6, 0x1f29: 0x00e6, - 0x1f2a: 0x00e6, 0x1f2b: 0x00e6, 0x1f2c: 0x00e6, 0x1f2d: 0x00e6, 0x1f2e: 0x00e6, 0x1f2f: 0x00e6, - 0x1f30: 0x00e6, 0x1f31: 0x00e6, - // Block 0x7d, offset 0x1f40 - 0x1f6b: 0x00dc, 0x1f6c: 0x00dc, 0x1f6d: 0x00dc, - // Block 0x7e, offset 0x1f80 - 0x1f93: 0x0009, - // Block 0x7f, offset 0x1fc0 - 0x1ff3: 0x0007, - // Block 0x80, offset 0x2000 - 0x2000: 0x0009, - // Block 0x81, offset 0x2040 - 0x2070: 0x00e6, 0x2072: 0x00e6, 0x2073: 0x00e6, 0x2074: 0x00dc, - 0x2077: 0x00e6, 0x2078: 0x00e6, - 0x207e: 0x00e6, 0x207f: 0x00e6, - // Block 0x82, offset 0x2080 - 0x2081: 0x00e6, - // Block 0x83, offset 0x20c0 - 0x20ed: 0x0009, - // Block 0x84, offset 0x2100 - 0x2100: 0x1100, 0x2101: 0x1100, 0x2102: 0x1100, 0x2103: 0x1100, 0x2104: 0x1100, 0x2105: 0x1100, - 0x2106: 0x1100, 0x2107: 0x1100, 0x2108: 0x1100, 0x2109: 0x1100, 0x210a: 0x1100, 0x210b: 0x1100, - 0x210c: 0x1100, 0x210d: 0x1100, 0x210e: 0x1100, 0x210f: 0x1100, 0x2110: 0x1100, 0x2111: 0x1100, - 0x2112: 0x1100, 0x2113: 0x1100, 0x2114: 0x1100, 0x2115: 0x1100, 0x2116: 0x1100, 0x2117: 0x1100, - 0x2118: 0x1100, 0x2119: 0x1100, 0x211a: 0x1100, 0x211b: 0x1100, 0x211c: 0x1100, 0x211d: 0x1100, - 0x211e: 0x1100, 0x211f: 0x1100, 0x2120: 0x1100, 0x2121: 0x1100, 0x2122: 0x1100, 0x2123: 0x1100, - 0x2124: 0x1100, 0x2125: 0x1100, 0x2126: 0x1100, 0x2127: 0x1100, 0x2128: 0x1100, 0x2129: 0x1100, - 0x212a: 0x1100, 0x212b: 0x1100, 0x212c: 0x1100, 0x212d: 0x1100, 0x212e: 0x1100, 0x212f: 0x1100, - 0x2130: 0x1100, 0x2131: 0x1100, 0x2132: 0x1100, 0x2133: 0x1100, 0x2134: 0x1100, 0x2135: 0x1100, - 0x2136: 0x1100, 0x2137: 0x1100, 0x2138: 0x1100, 0x2139: 0x1100, 0x213a: 0x1100, 0x213b: 0x1100, - 0x213c: 0x1100, 0x213d: 0x1100, 0x213e: 0x1100, 0x213f: 0x1100, - // Block 0x85, offset 0x2140 - 0x2140: 0x1100, 0x2141: 0x1100, 0x2142: 0x1100, 0x2143: 0x1100, 0x2144: 0x1100, 0x2145: 0x1100, - 0x2146: 0x1100, 0x2147: 0x1100, 0x2148: 0x1100, 0x2149: 0x1100, 0x214a: 0x1100, 0x214b: 0x1100, - 0x214c: 0x1100, 0x214d: 0x1100, 0x214e: 0x1100, 0x214f: 0x1100, 0x2150: 0x1100, 0x2151: 0x1100, - 0x2152: 0x1100, 0x2153: 0x1100, 0x2154: 0x1100, 0x2155: 0x1100, 0x2156: 0x1100, 0x2157: 0x1100, - 0x2158: 0x1100, 0x2159: 0x1100, 0x215a: 0x1100, 0x215b: 0x1100, 0x215c: 0x1100, 0x215d: 0x1100, - 0x215e: 0x1100, 0x215f: 0x1100, 0x2160: 0x1100, 0x2161: 0x1100, 0x2162: 0x1100, 0x2163: 0x1100, - // Block 0x86, offset 0x2180 - 0x2180: 0x3300, 0x2181: 0x3300, 0x2182: 0x3300, 0x2183: 0x3300, 0x2184: 0x3300, 0x2185: 0x3300, - 0x2186: 0x3300, 0x2187: 0x3300, 0x2188: 0x3300, 0x2189: 0x3300, 0x218a: 0x3300, 0x218b: 0x3300, - 0x218c: 0x3300, 0x218d: 0x3300, 0x218e: 0x3300, 0x218f: 0x3300, 0x2190: 0x3300, 0x2191: 0x3300, - 0x2192: 0x3300, 0x2193: 0x3300, 0x2194: 0x3300, 0x2195: 0x3300, 0x2196: 0x3300, 0x2197: 0x3300, - 0x2198: 0x3300, 0x2199: 0x3300, 0x219a: 0x3300, 0x219b: 0x3300, 0x219c: 0x3300, 0x219d: 0x3300, - 0x219e: 0x3300, 0x219f: 0x3300, 0x21a0: 0x3300, 0x21a1: 0x3300, 0x21a2: 0x3300, 0x21a3: 0x3300, - 0x21a4: 0x3300, 0x21a5: 0x3300, 0x21a6: 0x3300, 0x21a7: 0x3300, 0x21a8: 0x3300, 0x21a9: 0x3300, - 0x21aa: 0x3300, 0x21ab: 0x3300, 0x21ac: 0x3300, 0x21ad: 0x3300, 0x21ae: 0x3300, 0x21af: 0x3300, - 0x21b0: 0x3300, 0x21b1: 0x3300, 0x21b2: 0x3300, 0x21b3: 0x3300, 0x21b4: 0x3300, 0x21b5: 0x3300, - 0x21b6: 0x3300, 0x21b7: 0x3300, 0x21b8: 0x3300, 0x21b9: 0x3300, 0x21ba: 0x3300, 0x21bb: 0x3300, - 0x21bc: 0x3300, 0x21bd: 0x3300, 0x21be: 0x3300, 0x21bf: 0x3300, - // Block 0x87, offset 0x21c0 - 0x21c0: 0x3300, 0x21c1: 0x3300, 0x21c2: 0x3300, 0x21c3: 0x3300, 0x21c4: 0x3300, 0x21c5: 0x3300, - 0x21c6: 0x3300, 0x21c7: 0x3300, 0x21c8: 0x3300, 0x21c9: 0x3300, 0x21ca: 0x3300, 0x21cb: 0x3300, - 0x21cc: 0x3300, 0x21cd: 0x3300, 0x21d0: 0x3300, - 0x21d2: 0x3300, 0x21d5: 0x3300, 0x21d6: 0x3300, 0x21d7: 0x3300, - 0x21d8: 0x3300, 0x21d9: 0x3300, 0x21da: 0x3300, 0x21db: 0x3300, 0x21dc: 0x3300, 0x21dd: 0x3300, - 0x21de: 0x3300, 0x21e0: 0x3300, 0x21e2: 0x3300, - 0x21e5: 0x3300, 0x21e6: 0x3300, - 0x21ea: 0x3300, 0x21eb: 0x3300, 0x21ec: 0x3300, 0x21ed: 0x3300, - 0x21f0: 0x3300, 0x21f1: 0x3300, 0x21f2: 0x3300, 0x21f3: 0x3300, 0x21f4: 0x3300, 0x21f5: 0x3300, - 0x21f6: 0x3300, 0x21f7: 0x3300, 0x21f8: 0x3300, 0x21f9: 0x3300, 0x21fa: 0x3300, 0x21fb: 0x3300, - 0x21fc: 0x3300, 0x21fd: 0x3300, 0x21fe: 0x3300, 0x21ff: 0x3300, - // Block 0x88, offset 0x2200 - 0x2200: 0x3300, 0x2201: 0x3300, 0x2202: 0x3300, 0x2203: 0x3300, 0x2204: 0x3300, 0x2205: 0x3300, - 0x2206: 0x3300, 0x2207: 0x3300, 0x2208: 0x3300, 0x2209: 0x3300, 0x220a: 0x3300, 0x220b: 0x3300, - 0x220c: 0x3300, 0x220d: 0x3300, 0x220e: 0x3300, 0x220f: 0x3300, 0x2210: 0x3300, 0x2211: 0x3300, - 0x2212: 0x3300, 0x2213: 0x3300, 0x2214: 0x3300, 0x2215: 0x3300, 0x2216: 0x3300, 0x2217: 0x3300, - 0x2218: 0x3300, 0x2219: 0x3300, 0x221a: 0x3300, 0x221b: 0x3300, 0x221c: 0x3300, 0x221d: 0x3300, - 0x221e: 0x3300, 0x221f: 0x3300, 0x2220: 0x3300, 0x2221: 0x3300, 0x2222: 0x3300, 0x2223: 0x3300, - 0x2224: 0x3300, 0x2225: 0x3300, 0x2226: 0x3300, 0x2227: 0x3300, 0x2228: 0x3300, 0x2229: 0x3300, - 0x222a: 0x3300, 0x222b: 0x3300, 0x222c: 0x3300, 0x222d: 0x3300, - 0x2230: 0x3300, 0x2231: 0x3300, 0x2232: 0x3300, 0x2233: 0x3300, 0x2234: 0x3300, 0x2235: 0x3300, - 0x2236: 0x3300, 0x2237: 0x3300, 0x2238: 0x3300, 0x2239: 0x3300, 0x223a: 0x3300, 0x223b: 0x3300, - 0x223c: 0x3300, 0x223d: 0x3300, 0x223e: 0x3300, 0x223f: 0x3300, - // Block 0x89, offset 0x2240 - 0x2240: 0x3300, 0x2241: 0x3300, 0x2242: 0x3300, 0x2243: 0x3300, 0x2244: 0x3300, 0x2245: 0x3300, - 0x2246: 0x3300, 0x2247: 0x3300, 0x2248: 0x3300, 0x2249: 0x3300, 0x224a: 0x3300, 0x224b: 0x3300, - 0x224c: 0x3300, 0x224d: 0x3300, 0x224e: 0x3300, 0x224f: 0x3300, 0x2250: 0x3300, 0x2251: 0x3300, - 0x2252: 0x3300, 0x2253: 0x3300, 0x2254: 0x3300, 0x2255: 0x3300, 0x2256: 0x3300, 0x2257: 0x3300, - 0x2258: 0x3300, 0x2259: 0x3300, - // Block 0x8a, offset 0x2280 - 0x2280: 0x3000, 0x2281: 0x3000, 0x2282: 0x3000, 0x2283: 0x3000, 0x2284: 0x3000, 0x2285: 0x3000, - 0x2286: 0x3000, - 0x2293: 0x3000, 0x2294: 0x3000, 0x2295: 0x3000, 0x2296: 0x3000, 0x2297: 0x3000, - 0x229d: 0x3300, - 0x229e: 0x001a, 0x229f: 0x3300, 0x22a0: 0x3000, 0x22a1: 0x3000, 0x22a2: 0x3000, 0x22a3: 0x3000, - 0x22a4: 0x3000, 0x22a5: 0x3000, 0x22a6: 0x3000, 0x22a7: 0x3000, 0x22a8: 0x3000, 0x22a9: 0x3000, - 0x22aa: 0x3300, 0x22ab: 0x3300, 0x22ac: 0x3300, 0x22ad: 0x3300, 0x22ae: 0x3300, 0x22af: 0x3300, - 0x22b0: 0x3300, 0x22b1: 0x3300, 0x22b2: 0x3300, 0x22b3: 0x3300, 0x22b4: 0x3300, 0x22b5: 0x3300, - 0x22b6: 0x3300, 0x22b8: 0x3300, 0x22b9: 0x3300, 0x22ba: 0x3300, 0x22bb: 0x3300, - 0x22bc: 0x3300, 0x22be: 0x3300, - // Block 0x8b, offset 0x22c0 - 0x22c0: 0x3300, 0x22c1: 0x3300, 0x22c3: 0x3300, 0x22c4: 0x3300, - 0x22c6: 0x3300, 0x22c7: 0x3300, 0x22c8: 0x3300, 0x22c9: 0x3300, 0x22ca: 0x3300, 0x22cb: 0x3300, - 0x22cc: 0x3300, 0x22cd: 0x3300, 0x22ce: 0x3300, 0x22cf: 0x3000, 0x22d0: 0x3000, 0x22d1: 0x3000, - 0x22d2: 0x3000, 0x22d3: 0x3000, 0x22d4: 0x3000, 0x22d5: 0x3000, 0x22d6: 0x3000, 0x22d7: 0x3000, - 0x22d8: 0x3000, 0x22d9: 0x3000, 0x22da: 0x3000, 0x22db: 0x3000, 0x22dc: 0x3000, 0x22dd: 0x3000, - 0x22de: 0x3000, 0x22df: 0x3000, 0x22e0: 0x3000, 0x22e1: 0x3000, 0x22e2: 0x3000, 0x22e3: 0x3000, - 0x22e4: 0x3000, 0x22e5: 0x3000, 0x22e6: 0x3000, 0x22e7: 0x3000, 0x22e8: 0x3000, 0x22e9: 0x3000, - 0x22ea: 0x3000, 0x22eb: 0x3000, 0x22ec: 0x3000, 0x22ed: 0x3000, 0x22ee: 0x3000, 0x22ef: 0x3000, - 0x22f0: 0x3000, 0x22f1: 0x3000, 0x22f2: 0x3000, 0x22f3: 0x3000, 0x22f4: 0x3000, 0x22f5: 0x3000, - 0x22f6: 0x3000, 0x22f7: 0x3000, 0x22f8: 0x3000, 0x22f9: 0x3000, 0x22fa: 0x3000, 0x22fb: 0x3000, - 0x22fc: 0x3000, 0x22fd: 0x3000, 0x22fe: 0x3000, 0x22ff: 0x3000, - // Block 0x8c, offset 0x2300 - 0x2300: 0x3000, 0x2301: 0x3000, 0x2302: 0x3000, 0x2303: 0x3000, 0x2304: 0x3000, 0x2305: 0x3000, - 0x2306: 0x3000, 0x2307: 0x3000, 0x2308: 0x3000, 0x2309: 0x3000, 0x230a: 0x3000, 0x230b: 0x3000, - 0x230c: 0x3000, 0x230d: 0x3000, 0x230e: 0x3000, 0x230f: 0x3000, 0x2310: 0x3000, 0x2311: 0x3000, - 0x2312: 0x3000, 0x2313: 0x3000, 0x2314: 0x3000, 0x2315: 0x3000, 0x2316: 0x3000, 0x2317: 0x3000, - 0x2318: 0x3000, 0x2319: 0x3000, 0x231a: 0x3000, 0x231b: 0x3000, 0x231c: 0x3000, 0x231d: 0x3000, - 0x231e: 0x3000, 0x231f: 0x3000, 0x2320: 0x3000, 0x2321: 0x3000, 0x2322: 0x3000, 0x2323: 0x3000, - 0x2324: 0x3000, 0x2325: 0x3000, 0x2326: 0x3000, 0x2327: 0x3000, 0x2328: 0x3000, 0x2329: 0x3000, - 0x232a: 0x3000, 0x232b: 0x3000, 0x232c: 0x3000, 0x232d: 0x3000, 0x232e: 0x3000, 0x232f: 0x3000, - 0x2330: 0x3000, 0x2331: 0x3000, - // Block 0x8d, offset 0x2340 - 0x2353: 0x3000, 0x2354: 0x3000, 0x2355: 0x3000, 0x2356: 0x3000, 0x2357: 0x3000, - 0x2358: 0x3000, 0x2359: 0x3000, 0x235a: 0x3000, 0x235b: 0x3000, 0x235c: 0x3000, 0x235d: 0x3000, - 0x235e: 0x3000, 0x235f: 0x3000, 0x2360: 0x3000, 0x2361: 0x3000, 0x2362: 0x3000, 0x2363: 0x3000, - 0x2364: 0x3000, 0x2365: 0x3000, 0x2366: 0x3000, 0x2367: 0x3000, 0x2368: 0x3000, 0x2369: 0x3000, - 0x236a: 0x3000, 0x236b: 0x3000, 0x236c: 0x3000, 0x236d: 0x3000, 0x236e: 0x3000, 0x236f: 0x3000, - 0x2370: 0x3000, 0x2371: 0x3000, 0x2372: 0x3000, 0x2373: 0x3000, 0x2374: 0x3000, 0x2375: 0x3000, - 0x2376: 0x3000, 0x2377: 0x3000, 0x2378: 0x3000, 0x2379: 0x3000, 0x237a: 0x3000, 0x237b: 0x3000, - 0x237c: 0x3000, 0x237d: 0x3000, 0x237e: 0x3000, 0x237f: 0x3000, - // Block 0x8e, offset 0x2380 - 0x2380: 0x3000, 0x2381: 0x3000, 0x2382: 0x3000, 0x2383: 0x3000, 0x2384: 0x3000, 0x2385: 0x3000, - 0x2386: 0x3000, 0x2387: 0x3000, 0x2388: 0x3000, 0x2389: 0x3000, 0x238a: 0x3000, 0x238b: 0x3000, - 0x238c: 0x3000, 0x238d: 0x3000, 0x238e: 0x3000, 0x238f: 0x3000, 0x2390: 0x3000, 0x2391: 0x3000, - 0x2392: 0x3000, 0x2393: 0x3000, 0x2394: 0x3000, 0x2395: 0x3000, 0x2396: 0x3000, 0x2397: 0x3000, - 0x2398: 0x3000, 0x2399: 0x3000, 0x239a: 0x3000, 0x239b: 0x3000, 0x239c: 0x3000, 0x239d: 0x3000, - 0x239e: 0x3000, 0x239f: 0x3000, 0x23a0: 0x3000, 0x23a1: 0x3000, 0x23a2: 0x3000, 0x23a3: 0x3000, - 0x23a4: 0x3000, 0x23a5: 0x3000, 0x23a6: 0x3000, 0x23a7: 0x3000, 0x23a8: 0x3000, 0x23a9: 0x3000, - 0x23aa: 0x3000, 0x23ab: 0x3000, 0x23ac: 0x3000, 0x23ad: 0x3000, 0x23ae: 0x3000, 0x23af: 0x3000, - 0x23b0: 0x3000, 0x23b1: 0x3000, 0x23b2: 0x3000, 0x23b3: 0x3000, 0x23b4: 0x3000, 0x23b5: 0x3000, - 0x23b6: 0x3000, 0x23b7: 0x3000, 0x23b8: 0x3000, 0x23b9: 0x3000, 0x23ba: 0x3000, 0x23bb: 0x3000, - 0x23bc: 0x3000, 0x23bd: 0x3000, - // Block 0x8f, offset 0x23c0 - 0x23d0: 0x3000, 0x23d1: 0x3000, - 0x23d2: 0x3000, 0x23d3: 0x3000, 0x23d4: 0x3000, 0x23d5: 0x3000, 0x23d6: 0x3000, 0x23d7: 0x3000, - 0x23d8: 0x3000, 0x23d9: 0x3000, 0x23da: 0x3000, 0x23db: 0x3000, 0x23dc: 0x3000, 0x23dd: 0x3000, - 0x23de: 0x3000, 0x23df: 0x3000, 0x23e0: 0x3000, 0x23e1: 0x3000, 0x23e2: 0x3000, 0x23e3: 0x3000, - 0x23e4: 0x3000, 0x23e5: 0x3000, 0x23e6: 0x3000, 0x23e7: 0x3000, 0x23e8: 0x3000, 0x23e9: 0x3000, - 0x23ea: 0x3000, 0x23eb: 0x3000, 0x23ec: 0x3000, 0x23ed: 0x3000, 0x23ee: 0x3000, 0x23ef: 0x3000, - 0x23f0: 0x3000, 0x23f1: 0x3000, 0x23f2: 0x3000, 0x23f3: 0x3000, 0x23f4: 0x3000, 0x23f5: 0x3000, - 0x23f6: 0x3000, 0x23f7: 0x3000, 0x23f8: 0x3000, 0x23f9: 0x3000, 0x23fa: 0x3000, 0x23fb: 0x3000, - 0x23fc: 0x3000, 0x23fd: 0x3000, 0x23fe: 0x3000, 0x23ff: 0x3000, - // Block 0x90, offset 0x2400 - 0x2400: 0x3000, 0x2401: 0x3000, 0x2402: 0x3000, 0x2403: 0x3000, 0x2404: 0x3000, 0x2405: 0x3000, - 0x2406: 0x3000, 0x2407: 0x3000, 0x2408: 0x3000, 0x2409: 0x3000, 0x240a: 0x3000, 0x240b: 0x3000, - 0x240c: 0x3000, 0x240d: 0x3000, 0x240e: 0x3000, 0x240f: 0x3000, - 0x2412: 0x3000, 0x2413: 0x3000, 0x2414: 0x3000, 0x2415: 0x3000, 0x2416: 0x3000, 0x2417: 0x3000, - 0x2418: 0x3000, 0x2419: 0x3000, 0x241a: 0x3000, 0x241b: 0x3000, 0x241c: 0x3000, 0x241d: 0x3000, - 0x241e: 0x3000, 0x241f: 0x3000, 0x2420: 0x3000, 0x2421: 0x3000, 0x2422: 0x3000, 0x2423: 0x3000, - 0x2424: 0x3000, 0x2425: 0x3000, 0x2426: 0x3000, 0x2427: 0x3000, 0x2428: 0x3000, 0x2429: 0x3000, - 0x242a: 0x3000, 0x242b: 0x3000, 0x242c: 0x3000, 0x242d: 0x3000, 0x242e: 0x3000, 0x242f: 0x3000, - 0x2430: 0x3000, 0x2431: 0x3000, 0x2432: 0x3000, 0x2433: 0x3000, 0x2434: 0x3000, 0x2435: 0x3000, - 0x2436: 0x3000, 0x2437: 0x3000, 0x2438: 0x3000, 0x2439: 0x3000, 0x243a: 0x3000, 0x243b: 0x3000, - 0x243c: 0x3000, 0x243d: 0x3000, 0x243e: 0x3000, 0x243f: 0x3000, - // Block 0x91, offset 0x2440 - 0x2440: 0x3000, 0x2441: 0x3000, 0x2442: 0x3000, 0x2443: 0x3000, 0x2444: 0x3000, 0x2445: 0x3000, - 0x2446: 0x3000, 0x2447: 0x3000, - 0x2470: 0x3000, 0x2471: 0x3000, 0x2472: 0x3000, 0x2473: 0x3000, 0x2474: 0x3000, 0x2475: 0x3000, - 0x2476: 0x3000, 0x2477: 0x3000, 0x2478: 0x3000, 0x2479: 0x3000, 0x247a: 0x3000, 0x247b: 0x3000, - 0x247c: 0x3000, - // Block 0x92, offset 0x2480 - 0x2490: 0x3000, 0x2491: 0x3000, - 0x2492: 0x3000, 0x2493: 0x3000, 0x2494: 0x3000, 0x2495: 0x3000, 0x2496: 0x3000, 0x2497: 0x3000, - 0x2498: 0x3000, 0x2499: 0x3000, - 0x24a0: 0x00e6, 0x24a1: 0x00e6, 0x24a2: 0x00e6, 0x24a3: 0x00e6, - 0x24a4: 0x00e6, 0x24a5: 0x00e6, 0x24a6: 0x00e6, - 0x24b0: 0x3000, 0x24b1: 0x3000, 0x24b2: 0x3000, 0x24b3: 0x3000, 0x24b4: 0x3000, 0x24b5: 0x3000, - 0x24b6: 0x3000, 0x24b7: 0x3000, 0x24b8: 0x3000, 0x24b9: 0x3000, 0x24ba: 0x3000, 0x24bb: 0x3000, - 0x24bc: 0x3000, 0x24bd: 0x3000, 0x24be: 0x3000, 0x24bf: 0x3000, - // Block 0x93, offset 0x24c0 - 0x24c0: 0x3000, 0x24c1: 0x3000, 0x24c2: 0x3000, 0x24c3: 0x3000, 0x24c4: 0x3000, - 0x24c7: 0x3000, 0x24c8: 0x3000, 0x24c9: 0x3000, 0x24ca: 0x3000, 0x24cb: 0x3000, - 0x24cc: 0x3000, 0x24cd: 0x3000, 0x24ce: 0x3000, 0x24cf: 0x3000, 0x24d0: 0x3000, 0x24d1: 0x3000, - 0x24d2: 0x3000, 0x24d4: 0x3000, 0x24d5: 0x3000, 0x24d6: 0x3000, 0x24d7: 0x3000, - 0x24d8: 0x3000, 0x24d9: 0x3000, 0x24da: 0x3000, 0x24db: 0x3000, 0x24dc: 0x3000, 0x24dd: 0x3000, - 0x24de: 0x3000, 0x24df: 0x3000, 0x24e0: 0x3000, 0x24e1: 0x3000, 0x24e2: 0x3000, 0x24e3: 0x3000, - 0x24e4: 0x3000, 0x24e5: 0x3000, 0x24e6: 0x3000, 0x24e8: 0x3000, 0x24e9: 0x3000, - 0x24ea: 0x3000, 0x24eb: 0x3000, - 0x24f0: 0x3000, 0x24f1: 0x3000, 0x24f2: 0x3000, 0x24f4: 0x3000, - 0x24f6: 0x3000, 0x24f7: 0x3000, 0x24f8: 0x3000, 0x24f9: 0x3000, 0x24fa: 0x3000, 0x24fb: 0x3000, - 0x24fc: 0x3000, 0x24fd: 0x3000, 0x24fe: 0x3000, 0x24ff: 0x3000, - // Block 0x94, offset 0x2500 - 0x2500: 0x3000, 0x2501: 0x3000, 0x2502: 0x3000, 0x2503: 0x3000, 0x2504: 0x3000, 0x2505: 0x3000, - 0x2506: 0x3000, 0x2507: 0x3000, 0x2508: 0x3000, 0x2509: 0x3000, 0x250a: 0x3000, 0x250b: 0x3000, - 0x250c: 0x3000, 0x250d: 0x3000, 0x250e: 0x3000, 0x250f: 0x3000, 0x2510: 0x3000, 0x2511: 0x3000, - 0x2512: 0x3000, 0x2513: 0x3000, 0x2514: 0x3000, 0x2515: 0x3000, 0x2516: 0x3000, 0x2517: 0x3000, - 0x2518: 0x3000, 0x2519: 0x3000, 0x251a: 0x3000, 0x251b: 0x3000, 0x251c: 0x3000, 0x251d: 0x3000, - 0x251e: 0x3000, 0x251f: 0x3000, 0x2520: 0x3000, 0x2521: 0x3000, 0x2522: 0x3000, 0x2523: 0x3000, - 0x2524: 0x3000, 0x2525: 0x3000, 0x2526: 0x3000, 0x2527: 0x3000, 0x2528: 0x3000, 0x2529: 0x3000, - 0x252a: 0x3000, 0x252b: 0x3000, 0x252c: 0x3000, 0x252d: 0x3000, 0x252e: 0x3000, 0x252f: 0x3000, - 0x2530: 0x3000, 0x2531: 0x3000, 0x2532: 0x3000, 0x2533: 0x3000, 0x2534: 0x3000, 0x2535: 0x3000, - 0x2536: 0x3000, 0x2537: 0x3000, 0x2538: 0x3000, 0x2539: 0x3000, 0x253a: 0x3000, 0x253b: 0x3000, - 0x253c: 0x3000, - // Block 0x95, offset 0x2540 - 0x2541: 0x3000, 0x2542: 0x3000, 0x2543: 0x3000, 0x2544: 0x3000, 0x2545: 0x3000, - 0x2546: 0x3000, 0x2547: 0x3000, 0x2548: 0x3000, 0x2549: 0x3000, 0x254a: 0x3000, 0x254b: 0x3000, - 0x254c: 0x3000, 0x254d: 0x3000, 0x254e: 0x3000, 0x254f: 0x3000, 0x2550: 0x3000, 0x2551: 0x3000, - 0x2552: 0x3000, 0x2553: 0x3000, 0x2554: 0x3000, 0x2555: 0x3000, 0x2556: 0x3000, 0x2557: 0x3000, - 0x2558: 0x3000, 0x2559: 0x3000, 0x255a: 0x3000, 0x255b: 0x3000, 0x255c: 0x3000, 0x255d: 0x3000, - 0x255e: 0x3000, 0x255f: 0x3000, 0x2560: 0x3000, 0x2561: 0x3000, 0x2562: 0x3000, 0x2563: 0x3000, - 0x2564: 0x3000, 0x2565: 0x3000, 0x2566: 0x3000, 0x2567: 0x3000, 0x2568: 0x3000, 0x2569: 0x3000, - 0x256a: 0x3000, 0x256b: 0x3000, 0x256c: 0x3000, 0x256d: 0x3000, 0x256e: 0x3000, 0x256f: 0x3000, - 0x2570: 0x3000, 0x2571: 0x3000, 0x2572: 0x3000, 0x2573: 0x3000, 0x2574: 0x3000, 0x2575: 0x3000, - 0x2576: 0x3000, 0x2577: 0x3000, 0x2578: 0x3000, 0x2579: 0x3000, 0x257a: 0x3000, 0x257b: 0x3000, - 0x257c: 0x3000, 0x257d: 0x3000, 0x257e: 0x3000, 0x257f: 0x3000, - // Block 0x96, offset 0x2580 - 0x2582: 0x3000, 0x2583: 0x3000, 0x2584: 0x3000, 0x2585: 0x3000, - 0x2586: 0x3000, 0x2587: 0x3000, 0x258a: 0x3000, 0x258b: 0x3000, - 0x258c: 0x3000, 0x258d: 0x3000, 0x258e: 0x3000, 0x258f: 0x3000, - 0x2592: 0x3000, 0x2593: 0x3000, 0x2594: 0x3000, 0x2595: 0x3000, 0x2596: 0x3000, 0x2597: 0x3000, - 0x259a: 0x3000, 0x259b: 0x3000, 0x259c: 0x3000, - 0x25a0: 0x3000, 0x25a1: 0x3000, 0x25a2: 0x3000, 0x25a3: 0x3000, - 0x25a4: 0x3000, 0x25a5: 0x3000, 0x25a6: 0x3000, 0x25a8: 0x3000, 0x25a9: 0x3000, - 0x25aa: 0x3000, 0x25ab: 0x3000, 0x25ac: 0x3000, 0x25ad: 0x3000, 0x25ae: 0x3000, - // Block 0x97, offset 0x25c0 - 0x25fd: 0x00dc, - // Block 0x98, offset 0x2600 - 0x260d: 0x00dc, 0x260f: 0x00e6, - 0x2638: 0x00e6, 0x2639: 0x0001, 0x263a: 0x00dc, - 0x263f: 0x0009, - // Block 0x99, offset 0x2640 - 0x2659: 0x8800, 0x265a: 0x1100, 0x265b: 0x8800, 0x265c: 0x1100, - 0x2665: 0x8800, - 0x266b: 0x1100, - 0x2679: 0x0009, 0x267a: 0x6607, - // Block 0x9a, offset 0x2680 - 0x269e: 0x3300, 0x269f: 0x3300, 0x26a0: 0x3300, 0x26a1: 0x3300, 0x26a2: 0x3300, 0x26a3: 0x3300, - 0x26a4: 0x3300, 0x26a5: 0x00d8, 0x26a6: 0x00d8, 0x26a7: 0x0001, 0x26a8: 0x0001, 0x26a9: 0x0001, - 0x26ad: 0x00e2, 0x26ae: 0x00d8, 0x26af: 0x00d8, - 0x26b0: 0x00d8, 0x26b1: 0x00d8, 0x26b2: 0x00d8, - 0x26bb: 0x00dc, - 0x26bc: 0x00dc, 0x26bd: 0x00dc, 0x26be: 0x00dc, 0x26bf: 0x00dc, - // Block 0x9b, offset 0x26c0 - 0x26c0: 0x00dc, 0x26c1: 0x00dc, 0x26c2: 0x00dc, 0x26c5: 0x00e6, - 0x26c6: 0x00e6, 0x26c7: 0x00e6, 0x26c8: 0x00e6, 0x26c9: 0x00e6, 0x26ca: 0x00dc, 0x26cb: 0x00dc, - 0x26ea: 0x00e6, 0x26eb: 0x00e6, 0x26ec: 0x00e6, 0x26ed: 0x00e6, - 0x26fb: 0x3300, - 0x26fc: 0x3300, 0x26fd: 0x3300, 0x26fe: 0x3300, 0x26ff: 0x3300, - // Block 0x9c, offset 0x2700 - 0x2700: 0x3300, - // Block 0x9d, offset 0x2740 - 0x2742: 0x00e6, 0x2743: 0x00e6, 0x2744: 0x00e6, - // Block 0x9e, offset 0x2780 - 0x2780: 0x3000, 0x2781: 0x3000, 0x2782: 0x3000, 0x2783: 0x3000, 0x2784: 0x3000, 0x2785: 0x3000, - 0x2786: 0x3000, 0x2787: 0x3000, 0x2788: 0x3000, 0x2789: 0x3000, 0x278a: 0x3000, 0x278b: 0x3000, - 0x278c: 0x3000, 0x278d: 0x3000, 0x278e: 0x3000, 0x278f: 0x3000, 0x2790: 0x3000, 0x2791: 0x3000, - 0x2792: 0x3000, 0x2793: 0x3000, 0x2794: 0x3000, 0x2796: 0x3000, 0x2797: 0x3000, - 0x2798: 0x3000, 0x2799: 0x3000, 0x279a: 0x3000, 0x279b: 0x3000, 0x279c: 0x3000, 0x279d: 0x3000, - 0x279e: 0x3000, 0x279f: 0x3000, 0x27a0: 0x3000, 0x27a1: 0x3000, 0x27a2: 0x3000, 0x27a3: 0x3000, - 0x27a4: 0x3000, 0x27a5: 0x3000, 0x27a6: 0x3000, 0x27a7: 0x3000, 0x27a8: 0x3000, 0x27a9: 0x3000, - 0x27aa: 0x3000, 0x27ab: 0x3000, 0x27ac: 0x3000, 0x27ad: 0x3000, 0x27ae: 0x3000, 0x27af: 0x3000, - 0x27b0: 0x3000, 0x27b1: 0x3000, 0x27b2: 0x3000, 0x27b3: 0x3000, 0x27b4: 0x3000, 0x27b5: 0x3000, - 0x27b6: 0x3000, 0x27b7: 0x3000, 0x27b8: 0x3000, 0x27b9: 0x3000, 0x27ba: 0x3000, 0x27bb: 0x3000, - 0x27bc: 0x3000, 0x27bd: 0x3000, 0x27be: 0x3000, 0x27bf: 0x3000, - // Block 0x9f, offset 0x27c0 - 0x27c0: 0x3000, 0x27c1: 0x3000, 0x27c2: 0x3000, 0x27c3: 0x3000, 0x27c4: 0x3000, 0x27c5: 0x3000, - 0x27c6: 0x3000, 0x27c7: 0x3000, 0x27c8: 0x3000, 0x27c9: 0x3000, 0x27ca: 0x3000, 0x27cb: 0x3000, - 0x27cc: 0x3000, 0x27cd: 0x3000, 0x27ce: 0x3000, 0x27cf: 0x3000, 0x27d0: 0x3000, 0x27d1: 0x3000, - 0x27d2: 0x3000, 0x27d3: 0x3000, 0x27d4: 0x3000, 0x27d5: 0x3000, 0x27d6: 0x3000, 0x27d7: 0x3000, - 0x27d8: 0x3000, 0x27d9: 0x3000, 0x27da: 0x3000, 0x27db: 0x3000, 0x27dc: 0x3000, - 0x27de: 0x3000, 0x27df: 0x3000, 0x27e2: 0x3000, - 0x27e5: 0x3000, 0x27e6: 0x3000, 0x27e9: 0x3000, - 0x27ea: 0x3000, 0x27eb: 0x3000, 0x27ec: 0x3000, 0x27ee: 0x3000, 0x27ef: 0x3000, - 0x27f0: 0x3000, 0x27f1: 0x3000, 0x27f2: 0x3000, 0x27f3: 0x3000, 0x27f4: 0x3000, 0x27f5: 0x3000, - 0x27f6: 0x3000, 0x27f7: 0x3000, 0x27f8: 0x3000, 0x27f9: 0x3000, 0x27fb: 0x3000, - 0x27fd: 0x3000, 0x27fe: 0x3000, 0x27ff: 0x3000, - // Block 0xa0, offset 0x2800 - 0x2800: 0x3000, 0x2801: 0x3000, 0x2802: 0x3000, 0x2803: 0x3000, 0x2805: 0x3000, - 0x2806: 0x3000, 0x2807: 0x3000, 0x2808: 0x3000, 0x2809: 0x3000, 0x280a: 0x3000, 0x280b: 0x3000, - 0x280c: 0x3000, 0x280d: 0x3000, 0x280e: 0x3000, 0x280f: 0x3000, 0x2810: 0x3000, 0x2811: 0x3000, - 0x2812: 0x3000, 0x2813: 0x3000, 0x2814: 0x3000, 0x2815: 0x3000, 0x2816: 0x3000, 0x2817: 0x3000, - 0x2818: 0x3000, 0x2819: 0x3000, 0x281a: 0x3000, 0x281b: 0x3000, 0x281c: 0x3000, 0x281d: 0x3000, - 0x281e: 0x3000, 0x281f: 0x3000, 0x2820: 0x3000, 0x2821: 0x3000, 0x2822: 0x3000, 0x2823: 0x3000, - 0x2824: 0x3000, 0x2825: 0x3000, 0x2826: 0x3000, 0x2827: 0x3000, 0x2828: 0x3000, 0x2829: 0x3000, - 0x282a: 0x3000, 0x282b: 0x3000, 0x282c: 0x3000, 0x282d: 0x3000, 0x282e: 0x3000, 0x282f: 0x3000, - 0x2830: 0x3000, 0x2831: 0x3000, 0x2832: 0x3000, 0x2833: 0x3000, 0x2834: 0x3000, 0x2835: 0x3000, - 0x2836: 0x3000, 0x2837: 0x3000, 0x2838: 0x3000, 0x2839: 0x3000, 0x283a: 0x3000, 0x283b: 0x3000, - 0x283c: 0x3000, 0x283d: 0x3000, 0x283e: 0x3000, 0x283f: 0x3000, - // Block 0xa1, offset 0x2840 - 0x2840: 0x3000, 0x2841: 0x3000, 0x2842: 0x3000, 0x2843: 0x3000, 0x2844: 0x3000, 0x2845: 0x3000, - 0x2847: 0x3000, 0x2848: 0x3000, 0x2849: 0x3000, 0x284a: 0x3000, - 0x284d: 0x3000, 0x284e: 0x3000, 0x284f: 0x3000, 0x2850: 0x3000, 0x2851: 0x3000, - 0x2852: 0x3000, 0x2853: 0x3000, 0x2854: 0x3000, 0x2856: 0x3000, 0x2857: 0x3000, - 0x2858: 0x3000, 0x2859: 0x3000, 0x285a: 0x3000, 0x285b: 0x3000, 0x285c: 0x3000, - 0x285e: 0x3000, 0x285f: 0x3000, 0x2860: 0x3000, 0x2861: 0x3000, 0x2862: 0x3000, 0x2863: 0x3000, - 0x2864: 0x3000, 0x2865: 0x3000, 0x2866: 0x3000, 0x2867: 0x3000, 0x2868: 0x3000, 0x2869: 0x3000, - 0x286a: 0x3000, 0x286b: 0x3000, 0x286c: 0x3000, 0x286d: 0x3000, 0x286e: 0x3000, 0x286f: 0x3000, - 0x2870: 0x3000, 0x2871: 0x3000, 0x2872: 0x3000, 0x2873: 0x3000, 0x2874: 0x3000, 0x2875: 0x3000, - 0x2876: 0x3000, 0x2877: 0x3000, 0x2878: 0x3000, 0x2879: 0x3000, 0x287b: 0x3000, - 0x287c: 0x3000, 0x287d: 0x3000, 0x287e: 0x3000, - // Block 0xa2, offset 0x2880 - 0x2880: 0x3000, 0x2881: 0x3000, 0x2882: 0x3000, 0x2883: 0x3000, 0x2884: 0x3000, - 0x2886: 0x3000, 0x288a: 0x3000, 0x288b: 0x3000, - 0x288c: 0x3000, 0x288d: 0x3000, 0x288e: 0x3000, 0x288f: 0x3000, 0x2890: 0x3000, - 0x2892: 0x3000, 0x2893: 0x3000, 0x2894: 0x3000, 0x2895: 0x3000, 0x2896: 0x3000, 0x2897: 0x3000, - 0x2898: 0x3000, 0x2899: 0x3000, 0x289a: 0x3000, 0x289b: 0x3000, 0x289c: 0x3000, 0x289d: 0x3000, - 0x289e: 0x3000, 0x289f: 0x3000, 0x28a0: 0x3000, 0x28a1: 0x3000, 0x28a2: 0x3000, 0x28a3: 0x3000, - 0x28a4: 0x3000, 0x28a5: 0x3000, 0x28a6: 0x3000, 0x28a7: 0x3000, 0x28a8: 0x3000, 0x28a9: 0x3000, - 0x28aa: 0x3000, 0x28ab: 0x3000, 0x28ac: 0x3000, 0x28ad: 0x3000, 0x28ae: 0x3000, 0x28af: 0x3000, - 0x28b0: 0x3000, 0x28b1: 0x3000, 0x28b2: 0x3000, 0x28b3: 0x3000, 0x28b4: 0x3000, 0x28b5: 0x3000, - 0x28b6: 0x3000, 0x28b7: 0x3000, 0x28b8: 0x3000, 0x28b9: 0x3000, 0x28ba: 0x3000, 0x28bb: 0x3000, - 0x28bc: 0x3000, 0x28bd: 0x3000, 0x28be: 0x3000, 0x28bf: 0x3000, - // Block 0xa3, offset 0x28c0 - 0x28c0: 0x3000, 0x28c1: 0x3000, 0x28c2: 0x3000, 0x28c3: 0x3000, 0x28c4: 0x3000, 0x28c5: 0x3000, - 0x28c6: 0x3000, 0x28c7: 0x3000, 0x28c8: 0x3000, 0x28c9: 0x3000, 0x28ca: 0x3000, 0x28cb: 0x3000, - 0x28cc: 0x3000, 0x28cd: 0x3000, 0x28ce: 0x3000, 0x28cf: 0x3000, 0x28d0: 0x3000, 0x28d1: 0x3000, - 0x28d2: 0x3000, 0x28d3: 0x3000, 0x28d4: 0x3000, 0x28d5: 0x3000, 0x28d6: 0x3000, 0x28d7: 0x3000, - 0x28d8: 0x3000, 0x28d9: 0x3000, 0x28da: 0x3000, 0x28db: 0x3000, 0x28dc: 0x3000, 0x28dd: 0x3000, - 0x28de: 0x3000, 0x28df: 0x3000, 0x28e0: 0x3000, 0x28e1: 0x3000, 0x28e2: 0x3000, 0x28e3: 0x3000, - 0x28e4: 0x3000, 0x28e5: 0x3000, 0x28e8: 0x3000, 0x28e9: 0x3000, - 0x28ea: 0x3000, 0x28eb: 0x3000, 0x28ec: 0x3000, 0x28ed: 0x3000, 0x28ee: 0x3000, 0x28ef: 0x3000, - 0x28f0: 0x3000, 0x28f1: 0x3000, 0x28f2: 0x3000, 0x28f3: 0x3000, 0x28f4: 0x3000, 0x28f5: 0x3000, - 0x28f6: 0x3000, 0x28f7: 0x3000, 0x28f8: 0x3000, 0x28f9: 0x3000, 0x28fa: 0x3000, 0x28fb: 0x3000, - 0x28fc: 0x3000, 0x28fd: 0x3000, 0x28fe: 0x3000, 0x28ff: 0x3000, - // Block 0xa4, offset 0x2900 - 0x2900: 0x3000, 0x2901: 0x3000, 0x2902: 0x3000, 0x2903: 0x3000, 0x2904: 0x3000, 0x2905: 0x3000, - 0x2906: 0x3000, 0x2907: 0x3000, 0x2908: 0x3000, 0x2909: 0x3000, 0x290a: 0x3000, 0x290b: 0x3000, - 0x290e: 0x3000, 0x290f: 0x3000, 0x2910: 0x3000, 0x2911: 0x3000, - 0x2912: 0x3000, 0x2913: 0x3000, 0x2914: 0x3000, 0x2915: 0x3000, 0x2916: 0x3000, 0x2917: 0x3000, - 0x2918: 0x3000, 0x2919: 0x3000, 0x291a: 0x3000, 0x291b: 0x3000, 0x291c: 0x3000, 0x291d: 0x3000, - 0x291e: 0x3000, 0x291f: 0x3000, 0x2920: 0x3000, 0x2921: 0x3000, 0x2922: 0x3000, 0x2923: 0x3000, - 0x2924: 0x3000, 0x2925: 0x3000, 0x2926: 0x3000, 0x2927: 0x3000, 0x2928: 0x3000, 0x2929: 0x3000, - 0x292a: 0x3000, 0x292b: 0x3000, 0x292c: 0x3000, 0x292d: 0x3000, 0x292e: 0x3000, 0x292f: 0x3000, - 0x2930: 0x3000, 0x2931: 0x3000, 0x2932: 0x3000, 0x2933: 0x3000, 0x2934: 0x3000, 0x2935: 0x3000, - 0x2936: 0x3000, 0x2937: 0x3000, 0x2938: 0x3000, 0x2939: 0x3000, 0x293a: 0x3000, 0x293b: 0x3000, - 0x293c: 0x3000, 0x293d: 0x3000, 0x293e: 0x3000, 0x293f: 0x3000, - // Block 0xa5, offset 0x2940 - 0x2940: 0x3000, 0x2941: 0x3000, 0x2942: 0x3000, 0x2943: 0x3000, 0x2944: 0x3000, 0x2945: 0x3000, - 0x2946: 0x3000, 0x2947: 0x3000, 0x2948: 0x3000, 0x2949: 0x3000, 0x294a: 0x3000, - 0x2950: 0x3000, 0x2951: 0x3000, - 0x2952: 0x3000, 0x2953: 0x3000, 0x2954: 0x3000, 0x2955: 0x3000, 0x2956: 0x3000, 0x2957: 0x3000, - 0x2958: 0x3000, 0x2959: 0x3000, 0x295a: 0x3000, 0x295b: 0x3000, 0x295c: 0x3000, 0x295d: 0x3000, - 0x295e: 0x3000, 0x295f: 0x3000, 0x2960: 0x3000, 0x2961: 0x3000, 0x2962: 0x3000, 0x2963: 0x3000, - 0x2964: 0x3000, 0x2965: 0x3000, 0x2966: 0x3000, 0x2967: 0x3000, 0x2968: 0x3000, 0x2969: 0x3000, - 0x296a: 0x3000, 0x296b: 0x3000, 0x296c: 0x3000, 0x296d: 0x3000, 0x296e: 0x3000, - 0x2970: 0x3000, 0x2971: 0x3000, 0x2972: 0x3000, 0x2973: 0x3000, 0x2974: 0x3000, 0x2975: 0x3000, - 0x2976: 0x3000, 0x2977: 0x3000, 0x2978: 0x3000, 0x2979: 0x3000, 0x297a: 0x3000, 0x297b: 0x3000, - 0x297c: 0x3000, 0x297d: 0x3000, 0x297e: 0x3000, 0x297f: 0x3000, - // Block 0xa6, offset 0x2980 - 0x2980: 0x3000, 0x2981: 0x3000, 0x2982: 0x3000, 0x2983: 0x3000, 0x2984: 0x3000, 0x2985: 0x3000, - 0x2986: 0x3000, 0x2987: 0x3000, 0x2988: 0x3000, 0x2989: 0x3000, 0x298a: 0x3000, 0x298b: 0x3000, - 0x298c: 0x3000, 0x298d: 0x3000, 0x298e: 0x3000, 0x298f: 0x3000, - // Block 0xa7, offset 0x29c0 - 0x29d0: 0x3000, - // Block 0xa8, offset 0x2a00 - 0x2a00: 0x3000, 0x2a01: 0x3000, 0x2a02: 0x3000, - 0x2a10: 0x3000, 0x2a11: 0x3000, - 0x2a12: 0x3000, 0x2a13: 0x3000, 0x2a14: 0x3000, 0x2a15: 0x3000, 0x2a16: 0x3000, 0x2a17: 0x3000, - 0x2a18: 0x3000, 0x2a19: 0x3000, 0x2a1a: 0x3000, 0x2a1b: 0x3000, 0x2a1c: 0x3000, 0x2a1d: 0x3000, - 0x2a1e: 0x3000, 0x2a1f: 0x3000, 0x2a20: 0x3000, 0x2a21: 0x3000, 0x2a22: 0x3000, 0x2a23: 0x3000, - 0x2a24: 0x3000, 0x2a25: 0x3000, 0x2a26: 0x3000, 0x2a27: 0x3000, 0x2a28: 0x3000, 0x2a29: 0x3000, - 0x2a2a: 0x3000, 0x2a2b: 0x3000, 0x2a2c: 0x3000, 0x2a2d: 0x3000, 0x2a2e: 0x3000, 0x2a2f: 0x3000, - 0x2a30: 0x3000, 0x2a31: 0x3000, 0x2a32: 0x3000, 0x2a33: 0x3000, 0x2a34: 0x3000, 0x2a35: 0x3000, - 0x2a36: 0x3000, 0x2a37: 0x3000, 0x2a38: 0x3000, 0x2a39: 0x3000, 0x2a3a: 0x3000, - // Block 0xa9, offset 0x2a40 - 0x2a40: 0x3000, 0x2a41: 0x3000, 0x2a42: 0x3000, 0x2a43: 0x3000, 0x2a44: 0x3000, 0x2a45: 0x3000, - 0x2a46: 0x3000, 0x2a47: 0x3000, 0x2a48: 0x3000, - 0x2a50: 0x3000, 0x2a51: 0x3000, - // Block 0xaa, offset 0x2a80 - 0x2a80: 0x3300, 0x2a81: 0x3300, 0x2a82: 0x3300, 0x2a83: 0x3300, 0x2a84: 0x3300, 0x2a85: 0x3300, - 0x2a86: 0x3300, 0x2a87: 0x3300, 0x2a88: 0x3300, 0x2a89: 0x3300, 0x2a8a: 0x3300, 0x2a8b: 0x3300, - 0x2a8c: 0x3300, 0x2a8d: 0x3300, 0x2a8e: 0x3300, 0x2a8f: 0x3300, 0x2a90: 0x3300, 0x2a91: 0x3300, - 0x2a92: 0x3300, 0x2a93: 0x3300, 0x2a94: 0x3300, 0x2a95: 0x3300, 0x2a96: 0x3300, 0x2a97: 0x3300, - 0x2a98: 0x3300, 0x2a99: 0x3300, 0x2a9a: 0x3300, 0x2a9b: 0x3300, 0x2a9c: 0x3300, 0x2a9d: 0x3300, + 0x03c0: 0x1100, 0x03c1: 0x8800, 0x03c2: 0x1100, 0x03c4: 0x8800, 0x03c5: 0x1100, + 0x03c6: 0x8800, 0x03c7: 0x1100, 0x03c8: 0x8800, 0x03c9: 0x1100, + 0x03cf: 0x8800, 0x03d0: 0x1100, 0x03d1: 0x1100, + 0x03d2: 0x8800, 0x03d3: 0x1100, 0x03d4: 0x1100, 0x03d5: 0x8800, 0x03d6: 0x1100, 0x03d7: 0x1100, + 0x03d8: 0x8800, 0x03d9: 0x1100, 0x03da: 0x1100, 0x03db: 0x8800, 0x03dc: 0x1100, 0x03dd: 0x1100, + 0x03ef: 0x8800, + 0x03f0: 0x8800, 0x03f1: 0x8800, 0x03f2: 0x8800, 0x03f4: 0x1100, + 0x03f7: 0x1100, 0x03f8: 0x1100, 0x03f9: 0x1100, 0x03fa: 0x1100, + 0x03fd: 0x8800, 0x03fe: 0x1100, 0x03ff: 0x3000, +} + +// charInfoSparseOffset: 156 entries, 312 bytes +var charInfoSparseOffset = []uint16{0x0, 0x8, 0x13, 0x21, 0x25, 0x2f, 0x36, 0x39, 0x3c, 0x4a, 0x56, 0x58, 0x62, 0x67, 0x6e, 0x7d, 0x8a, 0x92, 0x96, 0x9b, 0x9d, 0xa5, 0xab, 0xae, 0xb5, 0xb9, 0xbd, 0xbf, 0xc1, 0xc8, 0xcc, 0xd1, 0xd7, 0xda, 0xe3, 0xe5, 0xed, 0xf1, 0xf3, 0xf6, 0xf9, 0xff, 0x10f, 0x11b, 0x11d, 0x123, 0x125, 0x127, 0x129, 0x12b, 0x12d, 0x12f, 0x131, 0x134, 0x137, 0x139, 0x13c, 0x13f, 0x143, 0x152, 0x15a, 0x15c, 0x15f, 0x161, 0x16a, 0x16e, 0x172, 0x174, 0x183, 0x187, 0x18d, 0x195, 0x199, 0x1a2, 0x1ab, 0x1b6, 0x1bc, 0x1c0, 0x1ce, 0x1dd, 0x1e1, 0x1e8, 0x1ed, 0x1fc, 0x208, 0x20b, 0x20d, 0x20f, 0x211, 0x213, 0x215, 0x217, 0x219, 0x21b, 0x21d, 0x220, 0x222, 0x224, 0x226, 0x228, 0x231, 0x233, 0x236, 0x239, 0x23c, 0x23e, 0x241, 0x243, 0x245, 0x247, 0x24a, 0x24c, 0x24e, 0x250, 0x252, 0x258, 0x25a, 0x25c, 0x25e, 0x260, 0x262, 0x26c, 0x26f, 0x271, 0x27b, 0x280, 0x282, 0x284, 0x286, 0x288, 0x28b, 0x28e, 0x292, 0x29a, 0x29c, 0x29e, 0x2a5, 0x2a7, 0x2ae, 0x2b6, 0x2bd, 0x2c3, 0x2c5, 0x2c7, 0x2ca, 0x2d3, 0x2d6, 0x2dd, 0x2e2, 0x2e5, 0x2e8, 0x2ec, 0x2ee, 0x2f0, 0x2f3, 0x2f6} + +// charInfoSparseValues: 760 entries, 3040 bytes +var charInfoSparseValues = [760]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0000, lo: 0x07}, + {value: 0x3000, lo: 0xa0, hi: 0xa0}, + {value: 0x3800, lo: 0xa8, hi: 0xa8}, + {value: 0x3000, lo: 0xaa, hi: 0xaa}, + {value: 0x3000, lo: 0xaf, hi: 0xaf}, + {value: 0x3000, lo: 0xb2, hi: 0xb5}, + {value: 0x3000, lo: 0xb8, hi: 0xba}, + {value: 0x3000, lo: 0xbc, hi: 0xbe}, + // Block 0x1, offset 0x2 + {value: 0x0000, lo: 0x0a}, + {value: 0x1100, lo: 0x80, hi: 0x81}, + {value: 0x9900, lo: 0x82, hi: 0x83}, + {value: 0x1100, lo: 0x84, hi: 0x8f}, + {value: 0x9900, lo: 0x92, hi: 0x93}, + {value: 0x1100, lo: 0x94, hi: 0xa5}, + {value: 0x1100, lo: 0xa8, hi: 0xb0}, + {value: 0x3000, lo: 0xb2, hi: 0xb3}, + {value: 0x1100, lo: 0xb4, hi: 0xb7}, + {value: 0x1100, lo: 0xb9, hi: 0xbe}, + {value: 0x3000, lo: 0xbf, hi: 0xbf}, + // Block 0x2, offset 0x3 + {value: 0x0000, lo: 0x0d}, + {value: 0x3000, lo: 0x80, hi: 0x80}, + {value: 0x1100, lo: 0x83, hi: 0x88}, + {value: 0x3000, lo: 0x89, hi: 0x89}, + {value: 0x9900, lo: 0x8c, hi: 0x8d}, + {value: 0x1100, lo: 0x8e, hi: 0x91}, + {value: 0x1100, lo: 0x94, hi: 0x99}, + {value: 0x9900, lo: 0x9a, hi: 0x9b}, + {value: 0x1100, lo: 0x9c, hi: 0x9f}, + {value: 0x9900, lo: 0xa0, hi: 0xa1}, + {value: 0x1100, lo: 0xa2, hi: 0xa5}, + {value: 0x9900, lo: 0xa8, hi: 0xab}, + {value: 0x1100, lo: 0xac, hi: 0xbe}, + {value: 0x3800, lo: 0xbf, hi: 0xbf}, + // Block 0x3, offset 0x4 + {value: 0x0000, lo: 0x03}, + {value: 0x9900, lo: 0xa0, hi: 0xa1}, + {value: 0x9900, lo: 0xaf, hi: 0xb0}, + {value: 0x8800, lo: 0xb7, hi: 0xb7}, + // Block 0x4, offset 0x5 + {value: 0x0000, lo: 0x09}, + {value: 0x3000, lo: 0x84, hi: 0x8c}, + {value: 0x1100, lo: 0x8d, hi: 0x9c}, + {value: 0x1100, lo: 0x9e, hi: 0xa3}, + {value: 0x1100, lo: 0xa6, hi: 0xa9}, + {value: 0x9900, lo: 0xaa, hi: 0xab}, + {value: 0x1100, lo: 0xac, hi: 0xb0}, + {value: 0x3000, lo: 0xb1, hi: 0xb3}, + {value: 0x1100, lo: 0xb4, hi: 0xb5}, + {value: 0x1100, lo: 0xb8, hi: 0xbf}, + // Block 0x5, offset 0x6 + {value: 0x0000, lo: 0x06}, + {value: 0x1100, lo: 0x80, hi: 0x9b}, + {value: 0x1100, lo: 0x9e, hi: 0x9f}, + {value: 0x9900, lo: 0xa6, hi: 0xa9}, + {value: 0x1100, lo: 0xaa, hi: 0xad}, + {value: 0x9900, lo: 0xae, hi: 0xaf}, + {value: 0x1100, lo: 0xb0, hi: 0xb3}, + // Block 0x6, offset 0x7 + {value: 0x0000, lo: 0x02}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x3000, lo: 0xb0, hi: 0xb8}, + // Block 0x7, offset 0x8 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x98, hi: 0x9d}, + {value: 0x3000, lo: 0xa0, hi: 0xa4}, + // Block 0x8, offset 0x9 + {value: 0x0000, lo: 0x0d}, + {value: 0x8800, lo: 0x81, hi: 0x81}, + {value: 0x8800, lo: 0x85, hi: 0x85}, + {value: 0x8800, lo: 0x89, hi: 0x89}, + {value: 0x9900, lo: 0x8a, hi: 0x8b}, + {value: 0x1100, lo: 0x8c, hi: 0x8d}, + {value: 0x9900, lo: 0x8e, hi: 0x8e}, + {value: 0x3000, lo: 0x90, hi: 0x91}, + {value: 0x3800, lo: 0x92, hi: 0x92}, + {value: 0x3100, lo: 0x93, hi: 0x94}, + {value: 0x3000, lo: 0x95, hi: 0x96}, + {value: 0x3000, lo: 0xb0, hi: 0xb2}, + {value: 0x3000, lo: 0xb4, hi: 0xb5}, + {value: 0x3000, lo: 0xb9, hi: 0xb9}, + // Block 0x9, offset 0xa + {value: 0x0000, lo: 0x0b}, + {value: 0x8800, lo: 0x83, hi: 0x83}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x8800, lo: 0x8b, hi: 0x8b}, + {value: 0x8800, lo: 0x8d, hi: 0x8d}, + {value: 0x1100, lo: 0x90, hi: 0x91}, + {value: 0x1100, lo: 0x93, hi: 0x93}, + {value: 0x8800, lo: 0x96, hi: 0x96}, + {value: 0x1100, lo: 0x97, hi: 0x97}, + {value: 0x1100, lo: 0x9c, hi: 0x9e}, + {value: 0x8800, lo: 0xb4, hi: 0xb5}, + {value: 0x1100, lo: 0xb6, hi: 0xb7}, + // Block 0xa, offset 0xb + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0x83, hi: 0x87}, + // Block 0xb, offset 0xc + {value: 0x0000, lo: 0x09}, + {value: 0x1100, lo: 0x81, hi: 0x82}, + {value: 0x1100, lo: 0x90, hi: 0x93}, + {value: 0x1100, lo: 0x96, hi: 0x97}, + {value: 0x8800, lo: 0x98, hi: 0x99}, + {value: 0x1100, lo: 0x9a, hi: 0x9f}, + {value: 0x1100, lo: 0xa2, hi: 0xa7}, + {value: 0x8800, lo: 0xa8, hi: 0xa9}, + {value: 0x1100, lo: 0xaa, hi: 0xb5}, + {value: 0x1100, lo: 0xb8, hi: 0xb9}, + // Block 0xc, offset 0xd + {value: 0x0001, lo: 0x04}, + {value: 0x0018, lo: 0x81, hi: 0x82}, + {value: 0x00e6, lo: 0x84, hi: 0x84}, + {value: 0x00dc, lo: 0x85, hi: 0x85}, + {value: 0x0012, lo: 0x87, hi: 0x87}, + // Block 0xd, offset 0xe + {value: 0x0000, lo: 0x06}, + {value: 0x00e6, lo: 0x90, hi: 0x97}, + {value: 0x001e, lo: 0x98, hi: 0x98}, + {value: 0x001f, lo: 0x99, hi: 0x99}, + {value: 0x0020, lo: 0x9a, hi: 0x9a}, + {value: 0x1100, lo: 0xa2, hi: 0xa6}, + {value: 0x8800, lo: 0xa7, hi: 0xa7}, + // Block 0xe, offset 0xf + {value: 0x0000, lo: 0x0e}, + {value: 0x1100, lo: 0x80, hi: 0x80}, + {value: 0x8800, lo: 0x81, hi: 0x81}, + {value: 0x1100, lo: 0x82, hi: 0x82}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x1100, lo: 0x93, hi: 0x93}, + {value: 0x8800, lo: 0x95, hi: 0x95}, + {value: 0x00e6, lo: 0x96, hi: 0x9c}, + {value: 0x00e6, lo: 0x9f, hi: 0xa2}, + {value: 0x00dc, lo: 0xa3, hi: 0xa3}, + {value: 0x00e6, lo: 0xa4, hi: 0xa4}, + {value: 0x00e6, lo: 0xa7, hi: 0xa8}, + {value: 0x00dc, lo: 0xaa, hi: 0xaa}, + {value: 0x00e6, lo: 0xab, hi: 0xac}, + {value: 0x00dc, lo: 0xad, hi: 0xad}, + // Block 0xf, offset 0x10 + {value: 0x0000, lo: 0x0c}, + {value: 0x0024, lo: 0x91, hi: 0x91}, + {value: 0x00e6, lo: 0xb0, hi: 0xb0}, + {value: 0x00dc, lo: 0xb1, hi: 0xb1}, + {value: 0x00e6, lo: 0xb2, hi: 0xb3}, + {value: 0x00dc, lo: 0xb4, hi: 0xb4}, + {value: 0x00e6, lo: 0xb5, hi: 0xb6}, + {value: 0x00dc, lo: 0xb7, hi: 0xb9}, + {value: 0x00e6, lo: 0xba, hi: 0xba}, + {value: 0x00dc, lo: 0xbb, hi: 0xbc}, + {value: 0x00e6, lo: 0xbd, hi: 0xbd}, + {value: 0x00dc, lo: 0xbe, hi: 0xbe}, + {value: 0x00e6, lo: 0xbf, hi: 0xbf}, + // Block 0x10, offset 0x11 + {value: 0x000a, lo: 0x07}, + {value: 0x00e6, lo: 0x80, hi: 0x80}, + {value: 0x00e6, lo: 0x81, hi: 0x81}, + {value: 0x00dc, lo: 0x82, hi: 0x83}, + {value: 0x00dc, lo: 0x84, hi: 0x85}, + {value: 0x00dc, lo: 0x86, hi: 0x87}, + {value: 0x00dc, lo: 0x88, hi: 0x89}, + {value: 0x00e6, lo: 0x8a, hi: 0x8a}, + // Block 0x11, offset 0x12 + {value: 0x0000, lo: 0x03}, + {value: 0x00e6, lo: 0xab, hi: 0xb1}, + {value: 0x00dc, lo: 0xb2, hi: 0xb2}, + {value: 0x00e6, lo: 0xb3, hi: 0xb3}, + // Block 0x12, offset 0x13 + {value: 0x0000, lo: 0x04}, + {value: 0x00e6, lo: 0x96, hi: 0x99}, + {value: 0x00e6, lo: 0x9b, hi: 0xa3}, + {value: 0x00e6, lo: 0xa5, hi: 0xa7}, + {value: 0x00e6, lo: 0xa9, hi: 0xad}, + // Block 0x13, offset 0x14 + {value: 0x0000, lo: 0x01}, + {value: 0x00dc, lo: 0x99, hi: 0x9b}, + // Block 0x14, offset 0x15 + {value: 0x7700, lo: 0x07}, + {value: 0x8800, lo: 0xa8, hi: 0xa8}, + {value: 0x1100, lo: 0xa9, hi: 0xa9}, + {value: 0x8800, lo: 0xb0, hi: 0xb0}, + {value: 0x1100, lo: 0xb1, hi: 0xb1}, + {value: 0x8800, lo: 0xb3, hi: 0xb3}, + {value: 0x1100, lo: 0xb4, hi: 0xb4}, + {value: 0x6607, lo: 0xbc, hi: 0xbc}, + // Block 0x15, offset 0x16 + {value: 0x0000, lo: 0x05}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x00e6, lo: 0x91, hi: 0x91}, + {value: 0x00dc, lo: 0x92, hi: 0x92}, + {value: 0x00e6, lo: 0x93, hi: 0x94}, + {value: 0x3300, lo: 0x98, hi: 0x9f}, + // Block 0x16, offset 0x17 + {value: 0x65f9, lo: 0x02}, + {value: 0x0007, lo: 0xbc, hi: 0xbc}, + {value: 0x6600, lo: 0xbe, hi: 0xbe}, + // Block 0x17, offset 0x18 + {value: 0x0000, lo: 0x06}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x1100, lo: 0x8b, hi: 0x8c}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x6600, lo: 0x97, hi: 0x97}, + {value: 0x3300, lo: 0x9c, hi: 0x9d}, + {value: 0x3300, lo: 0x9f, hi: 0x9f}, + // Block 0x18, offset 0x19 + {value: 0x0000, lo: 0x03}, + {value: 0x3300, lo: 0xb3, hi: 0xb3}, + {value: 0x3300, lo: 0xb6, hi: 0xb6}, + {value: 0x0007, lo: 0xbc, hi: 0xbc}, + // Block 0x19, offset 0x1a + {value: 0x0000, lo: 0x03}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x3300, lo: 0x99, hi: 0x9b}, + {value: 0x3300, lo: 0x9e, hi: 0x9e}, + // Block 0x1a, offset 0x1b + {value: 0x0000, lo: 0x01}, + {value: 0x0007, lo: 0xbc, hi: 0xbc}, + // Block 0x1b, offset 0x1c + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + // Block 0x1c, offset 0x1d + {value: 0x0000, lo: 0x06}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x1100, lo: 0x88, hi: 0x88}, + {value: 0x1100, lo: 0x8b, hi: 0x8c}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x6600, lo: 0x96, hi: 0x97}, + {value: 0x3300, lo: 0x9c, hi: 0x9d}, + // Block 0x1d, offset 0x1e + {value: 0x5500, lo: 0x03}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x1100, lo: 0x94, hi: 0x94}, + {value: 0x6600, lo: 0xbe, hi: 0xbe}, + // Block 0x1e, offset 0x1f + {value: 0x0000, lo: 0x04}, + {value: 0x8800, lo: 0x86, hi: 0x87}, + {value: 0x1100, lo: 0x8a, hi: 0x8c}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x6600, lo: 0x97, hi: 0x97}, + // Block 0x1f, offset 0x20 + {value: 0x004b, lo: 0x05}, + {value: 0x8800, lo: 0x86, hi: 0x86}, + {value: 0x1100, lo: 0x88, hi: 0x88}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x0054, lo: 0x95, hi: 0x95}, + {value: 0x665b, lo: 0x96, hi: 0x96}, + // Block 0x20, offset 0x21 + {value: 0x87f9, lo: 0x02}, + {value: 0x0007, lo: 0xbc, hi: 0xbc}, + {value: 0x8800, lo: 0xbf, hi: 0xbf}, + // Block 0x21, offset 0x22 + {value: 0x0000, lo: 0x08}, + {value: 0x1100, lo: 0x80, hi: 0x80}, + {value: 0x6600, lo: 0x82, hi: 0x82}, + {value: 0x8800, lo: 0x86, hi: 0x86}, + {value: 0x1100, lo: 0x87, hi: 0x88}, + {value: 0x9900, lo: 0x8a, hi: 0x8a}, + {value: 0x1100, lo: 0x8b, hi: 0x8b}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x6600, lo: 0x95, hi: 0x96}, + // Block 0x22, offset 0x23 + {value: 0x0000, lo: 0x01}, + {value: 0x6600, lo: 0xbe, hi: 0xbe}, + // Block 0x23, offset 0x24 + {value: 0x0000, lo: 0x07}, + {value: 0x6609, lo: 0x8a, hi: 0x8a}, + {value: 0x6600, lo: 0x8f, hi: 0x8f}, + {value: 0x8800, lo: 0x99, hi: 0x99}, + {value: 0x1100, lo: 0x9a, hi: 0x9a}, + {value: 0x9900, lo: 0x9c, hi: 0x9c}, + {value: 0x1100, lo: 0x9d, hi: 0x9e}, + {value: 0x6600, lo: 0x9f, hi: 0x9f}, + // Block 0x24, offset 0x25 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0xb3, hi: 0xb3}, + {value: 0x0067, lo: 0xb8, hi: 0xb9}, + {value: 0x0009, lo: 0xba, hi: 0xba}, + // Block 0x25, offset 0x26 + {value: 0x0000, lo: 0x01}, + {value: 0x006b, lo: 0x88, hi: 0x8b}, + // Block 0x26, offset 0x27 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0xb3, hi: 0xb3}, + {value: 0x0076, lo: 0xb8, hi: 0xb9}, + // Block 0x27, offset 0x28 + {value: 0x0000, lo: 0x02}, + {value: 0x007a, lo: 0x88, hi: 0x8b}, + {value: 0x3000, lo: 0x9c, hi: 0x9d}, + // Block 0x28, offset 0x29 + {value: 0x0000, lo: 0x05}, + {value: 0x3000, lo: 0x8c, hi: 0x8c}, + {value: 0x00dc, lo: 0x98, hi: 0x99}, + {value: 0x00dc, lo: 0xb5, hi: 0xb5}, + {value: 0x00dc, lo: 0xb7, hi: 0xb7}, + {value: 0x00d8, lo: 0xb9, hi: 0xb9}, + // Block 0x29, offset 0x2a + {value: 0x0000, lo: 0x0f}, + {value: 0x3300, lo: 0x83, hi: 0x83}, + {value: 0x3300, lo: 0x8d, hi: 0x8d}, + {value: 0x3300, lo: 0x92, hi: 0x92}, + {value: 0x3300, lo: 0x97, hi: 0x97}, + {value: 0x3300, lo: 0x9c, hi: 0x9c}, + {value: 0x3300, lo: 0xa9, hi: 0xa9}, + {value: 0x0081, lo: 0xb1, hi: 0xb1}, + {value: 0x0082, lo: 0xb2, hi: 0xb2}, + {value: 0x3300, lo: 0xb3, hi: 0xb3}, + {value: 0x0084, lo: 0xb4, hi: 0xb4}, + {value: 0x3300, lo: 0xb5, hi: 0xb6}, + {value: 0x3000, lo: 0xb7, hi: 0xb7}, + {value: 0x3300, lo: 0xb8, hi: 0xb8}, + {value: 0x3000, lo: 0xb9, hi: 0xb9}, + {value: 0x0082, lo: 0xba, hi: 0xbd}, + // Block 0x2a, offset 0x2b + {value: 0x0000, lo: 0x0b}, + {value: 0x0082, lo: 0x80, hi: 0x80}, + {value: 0x3300, lo: 0x81, hi: 0x81}, + {value: 0x00e6, lo: 0x82, hi: 0x83}, + {value: 0x0009, lo: 0x84, hi: 0x84}, + {value: 0x00e6, lo: 0x86, hi: 0x87}, + {value: 0x3300, lo: 0x93, hi: 0x93}, + {value: 0x3300, lo: 0x9d, hi: 0x9d}, + {value: 0x3300, lo: 0xa2, hi: 0xa2}, + {value: 0x3300, lo: 0xa7, hi: 0xa7}, + {value: 0x3300, lo: 0xac, hi: 0xac}, + {value: 0x3300, lo: 0xb9, hi: 0xb9}, + // Block 0x2b, offset 0x2c + {value: 0x0000, lo: 0x01}, + {value: 0x00dc, lo: 0x86, hi: 0x86}, + // Block 0x2c, offset 0x2d + {value: 0x0000, lo: 0x05}, + {value: 0x8800, lo: 0xa5, hi: 0xa5}, + {value: 0x1100, lo: 0xa6, hi: 0xa6}, + {value: 0x6600, lo: 0xae, hi: 0xae}, + {value: 0x0007, lo: 0xb7, hi: 0xb7}, + {value: 0x0009, lo: 0xb9, hi: 0xba}, + // Block 0x2d, offset 0x2e + {value: 0x0000, lo: 0x01}, + {value: 0x00dc, lo: 0x8d, hi: 0x8d}, + // Block 0x2e, offset 0x2f + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xbc, hi: 0xbc}, + // Block 0x2f, offset 0x30 + {value: 0x0000, lo: 0x01}, + {value: 0x8800, lo: 0x80, hi: 0x92}, + // Block 0x30, offset 0x31 + {value: 0x0000, lo: 0x01}, + {value: 0xee00, lo: 0xa1, hi: 0xb5}, + // Block 0x31, offset 0x32 + {value: 0x0000, lo: 0x01}, + {value: 0x6600, lo: 0xa8, hi: 0xbf}, + // Block 0x32, offset 0x33 + {value: 0x0000, lo: 0x01}, + {value: 0x6600, lo: 0x80, hi: 0x82}, + // Block 0x33, offset 0x34 + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0x9d, hi: 0x9f}, + // Block 0x34, offset 0x35 + {value: 0x0000, lo: 0x02}, + {value: 0x0009, lo: 0x94, hi: 0x94}, + {value: 0x0009, lo: 0xb4, hi: 0xb4}, + // Block 0x35, offset 0x36 + {value: 0x00dd, lo: 0x02}, + {value: 0x0009, lo: 0x92, hi: 0x92}, + {value: 0x00e6, lo: 0x9d, hi: 0x9d}, + // Block 0x36, offset 0x37 + {value: 0x0000, lo: 0x01}, + {value: 0x00e4, lo: 0xa9, hi: 0xa9}, + // Block 0x37, offset 0x38 + {value: 0x0008, lo: 0x02}, + {value: 0x00de, lo: 0xb9, hi: 0xba}, + {value: 0x00dc, lo: 0xbb, hi: 0xbb}, + // Block 0x38, offset 0x39 + {value: 0x0000, lo: 0x02}, + {value: 0x00e6, lo: 0x97, hi: 0x97}, + {value: 0x00dc, lo: 0x98, hi: 0x98}, + // Block 0x39, offset 0x3a + {value: 0x0000, lo: 0x03}, + {value: 0x0009, lo: 0xa0, hi: 0xa0}, + {value: 0x00e6, lo: 0xb5, hi: 0xbc}, + {value: 0x00dc, lo: 0xbf, hi: 0xbf}, + // Block 0x3a, offset 0x3b + {value: 0x7700, lo: 0x0e}, + {value: 0x8800, lo: 0x85, hi: 0x85}, + {value: 0x1100, lo: 0x86, hi: 0x87}, + {value: 0x1100, lo: 0x88, hi: 0x89}, + {value: 0x1100, lo: 0x8a, hi: 0x8b}, + {value: 0x1100, lo: 0x8c, hi: 0x8d}, + {value: 0x1100, lo: 0x8e, hi: 0x8e}, + {value: 0x8800, lo: 0x91, hi: 0x91}, + {value: 0x1100, lo: 0x92, hi: 0x92}, + {value: 0x0007, lo: 0xb4, hi: 0xb4}, + {value: 0x6600, lo: 0xb5, hi: 0xb5}, + {value: 0x8800, lo: 0xba, hi: 0xba}, + {value: 0x1100, lo: 0xbb, hi: 0xbc}, + {value: 0x1100, lo: 0xbd, hi: 0xbe}, + {value: 0x8800, lo: 0xbf, hi: 0xbf}, + // Block 0x3b, offset 0x3c + {value: 0x0000, lo: 0x07}, + {value: 0x1100, lo: 0x80, hi: 0x81}, + {value: 0x8800, lo: 0x82, hi: 0x82}, + {value: 0x1100, lo: 0x83, hi: 0x83}, + {value: 0x0009, lo: 0x84, hi: 0x84}, + {value: 0x00e6, lo: 0xab, hi: 0xab}, + {value: 0x00dc, lo: 0xac, hi: 0xac}, + {value: 0x00e6, lo: 0xad, hi: 0xb3}, + // Block 0x3c, offset 0x3d + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0xaa, hi: 0xaa}, + // Block 0x3d, offset 0x3e + {value: 0x0000, lo: 0x02}, + {value: 0x0007, lo: 0xa6, hi: 0xa6}, + {value: 0x0009, lo: 0xb2, hi: 0xb3}, + // Block 0x3e, offset 0x3f + {value: 0x0000, lo: 0x01}, + {value: 0x0007, lo: 0xb7, hi: 0xb7}, + // Block 0x3f, offset 0x40 + {value: 0x0000, lo: 0x08}, + {value: 0x00e6, lo: 0x90, hi: 0x92}, + {value: 0x0001, lo: 0x94, hi: 0x94}, + {value: 0x00dc, lo: 0x95, hi: 0x99}, + {value: 0x00e6, lo: 0x9a, hi: 0x9b}, + {value: 0x00dc, lo: 0x9c, hi: 0x9f}, + {value: 0x00e6, lo: 0xa0, hi: 0xa0}, + {value: 0x0001, lo: 0xa2, hi: 0xa8}, + {value: 0x00dc, lo: 0xad, hi: 0xad}, + // Block 0x40, offset 0x41 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0xac, hi: 0xae}, + {value: 0x3000, lo: 0xb0, hi: 0xba}, + {value: 0x3000, lo: 0xbc, hi: 0xbf}, + // Block 0x41, offset 0x42 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0x80, hi: 0x8d}, + {value: 0x3000, lo: 0x8f, hi: 0xaa}, + {value: 0x3000, lo: 0xb8, hi: 0xb8}, + // Block 0x42, offset 0x43 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x9b, hi: 0xbf}, + // Block 0x43, offset 0x44 + {value: 0x0000, lo: 0x0e}, + {value: 0x00e6, lo: 0x80, hi: 0x81}, + {value: 0x00dc, lo: 0x82, hi: 0x82}, + {value: 0x00e6, lo: 0x83, hi: 0x89}, + {value: 0x00dc, lo: 0x8a, hi: 0x8a}, + {value: 0x00e6, lo: 0x8b, hi: 0x8c}, + {value: 0x00ea, lo: 0x8d, hi: 0x8d}, + {value: 0x00d6, lo: 0x8e, hi: 0x8e}, + {value: 0x00dc, lo: 0x8f, hi: 0x8f}, + {value: 0x00ca, lo: 0x90, hi: 0x90}, + {value: 0x00e6, lo: 0x91, hi: 0xa6}, + {value: 0x00e9, lo: 0xbc, hi: 0xbc}, + {value: 0x00dc, lo: 0xbd, hi: 0xbd}, + {value: 0x00e6, lo: 0xbe, hi: 0xbe}, + {value: 0x00dc, lo: 0xbf, hi: 0xbf}, + // Block 0x44, offset 0x45 + {value: 0x0000, lo: 0x03}, + {value: 0x1100, lo: 0x80, hi: 0xb5}, + {value: 0x9900, lo: 0xb6, hi: 0xb7}, + {value: 0x1100, lo: 0xb8, hi: 0xbf}, + // Block 0x45, offset 0x46 + {value: 0x0000, lo: 0x05}, + {value: 0x1100, lo: 0x80, hi: 0x99}, + {value: 0x9900, lo: 0x9a, hi: 0x9b}, + {value: 0x1100, lo: 0x9c, hi: 0xa1}, + {value: 0x9900, lo: 0xa2, hi: 0xa3}, + {value: 0x1100, lo: 0xa4, hi: 0xbf}, + // Block 0x46, offset 0x47 + {value: 0x0000, lo: 0x07}, + {value: 0x1100, lo: 0x80, hi: 0x99}, + {value: 0x3000, lo: 0x9a, hi: 0x9a}, + {value: 0x3100, lo: 0x9b, hi: 0x9b}, + {value: 0x9900, lo: 0xa0, hi: 0xa1}, + {value: 0x1100, lo: 0xa2, hi: 0xb7}, + {value: 0x9900, lo: 0xb8, hi: 0xb9}, + {value: 0x1100, lo: 0xba, hi: 0xbf}, + // Block 0x47, offset 0x48 + {value: 0x0000, lo: 0x03}, + {value: 0x1100, lo: 0x80, hi: 0x8b}, + {value: 0x9900, lo: 0x8c, hi: 0x8d}, + {value: 0x1100, lo: 0x8e, hi: 0xb9}, + // Block 0x48, offset 0x49 + {value: 0x0000, lo: 0x08}, + {value: 0x9900, lo: 0x80, hi: 0x91}, + {value: 0x1100, lo: 0x92, hi: 0x95}, + {value: 0x9900, lo: 0x98, hi: 0x99}, + {value: 0x1100, lo: 0x9a, hi: 0x9d}, + {value: 0x9900, lo: 0xa0, hi: 0xb1}, + {value: 0x1100, lo: 0xb2, hi: 0xb7}, + {value: 0x9900, lo: 0xb8, hi: 0xb9}, + {value: 0x1100, lo: 0xba, hi: 0xbf}, + // Block 0x49, offset 0x4a + {value: 0x0000, lo: 0x08}, + {value: 0x1100, lo: 0x80, hi: 0xb4}, + {value: 0x9900, lo: 0xb6, hi: 0xb6}, + {value: 0x1100, lo: 0xb7, hi: 0xba}, + {value: 0x3300, lo: 0xbb, hi: 0xbb}, + {value: 0x1100, lo: 0xbc, hi: 0xbc}, + {value: 0x3000, lo: 0xbd, hi: 0xbd}, + {value: 0x3300, lo: 0xbe, hi: 0xbe}, + {value: 0x3800, lo: 0xbf, hi: 0xbf}, + // Block 0x4a, offset 0x4b + {value: 0x0000, lo: 0x0a}, + {value: 0x3300, lo: 0x80, hi: 0x81}, + {value: 0x3000, lo: 0x82, hi: 0x8a}, + {value: 0x3000, lo: 0x91, hi: 0x91}, + {value: 0x3000, lo: 0x97, hi: 0x97}, + {value: 0x3000, lo: 0xa4, hi: 0xa6}, + {value: 0x3000, lo: 0xaf, hi: 0xaf}, + {value: 0x3000, lo: 0xb3, hi: 0xb4}, + {value: 0x3000, lo: 0xb6, hi: 0xb7}, + {value: 0x3000, lo: 0xbc, hi: 0xbc}, + {value: 0x3000, lo: 0xbe, hi: 0xbe}, + // Block 0x4b, offset 0x4c + {value: 0x0000, lo: 0x05}, + {value: 0x3000, lo: 0x87, hi: 0x89}, + {value: 0x3000, lo: 0x97, hi: 0x97}, + {value: 0x3000, lo: 0x9f, hi: 0x9f}, + {value: 0x3000, lo: 0xb0, hi: 0xb1}, + {value: 0x3000, lo: 0xb4, hi: 0xbf}, + // Block 0x4c, offset 0x4d + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0x80, hi: 0x8e}, + {value: 0x3000, lo: 0x90, hi: 0x9c}, + {value: 0x3000, lo: 0xa8, hi: 0xa8}, + // Block 0x4d, offset 0x4e + {value: 0x0000, lo: 0x0d}, + {value: 0x00e6, lo: 0x90, hi: 0x91}, + {value: 0x0001, lo: 0x92, hi: 0x93}, + {value: 0x00e6, lo: 0x94, hi: 0x97}, + {value: 0x0001, lo: 0x98, hi: 0x9a}, + {value: 0x00e6, lo: 0x9b, hi: 0x9c}, + {value: 0x00e6, lo: 0xa1, hi: 0xa1}, + {value: 0x0001, lo: 0xa5, hi: 0xa6}, + {value: 0x00e6, lo: 0xa7, hi: 0xa7}, + {value: 0x00dc, lo: 0xa8, hi: 0xa8}, + {value: 0x00e6, lo: 0xa9, hi: 0xa9}, + {value: 0x0001, lo: 0xaa, hi: 0xab}, + {value: 0x00dc, lo: 0xac, hi: 0xaf}, + {value: 0x00e6, lo: 0xb0, hi: 0xb0}, + // Block 0x4e, offset 0x4f + {value: 0x0000, lo: 0x0e}, + {value: 0x3000, lo: 0x80, hi: 0x83}, + {value: 0x3000, lo: 0x85, hi: 0x87}, + {value: 0x3000, lo: 0x89, hi: 0x93}, + {value: 0x3000, lo: 0x95, hi: 0x96}, + {value: 0x3000, lo: 0x99, hi: 0x9d}, + {value: 0x3000, lo: 0xa0, hi: 0xa2}, + {value: 0x3000, lo: 0xa4, hi: 0xa4}, + {value: 0x3300, lo: 0xa6, hi: 0xa6}, + {value: 0x3000, lo: 0xa8, hi: 0xa8}, + {value: 0x3300, lo: 0xaa, hi: 0xab}, + {value: 0x3000, lo: 0xac, hi: 0xad}, + {value: 0x3000, lo: 0xaf, hi: 0xb1}, + {value: 0x3000, lo: 0xb3, hi: 0xb9}, + {value: 0x3000, lo: 0xbb, hi: 0xbf}, + // Block 0x4f, offset 0x50 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0x80, hi: 0x80}, + {value: 0x3000, lo: 0x85, hi: 0x89}, + {value: 0x3000, lo: 0x90, hi: 0xbf}, + // Block 0x50, offset 0x51 + {value: 0x0000, lo: 0x06}, + {value: 0x3000, lo: 0x89, hi: 0x89}, + {value: 0x8800, lo: 0x90, hi: 0x90}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x8800, lo: 0x94, hi: 0x94}, + {value: 0x1100, lo: 0x9a, hi: 0x9b}, + {value: 0x1100, lo: 0xae, hi: 0xae}, + // Block 0x51, offset 0x52 + {value: 0x0000, lo: 0x04}, + {value: 0x1100, lo: 0x8d, hi: 0x8f}, + {value: 0x8800, lo: 0x90, hi: 0x90}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x8800, lo: 0x94, hi: 0x94}, + // Block 0x52, offset 0x53 + {value: 0x7700, lo: 0x0e}, + {value: 0x8800, lo: 0x83, hi: 0x83}, + {value: 0x1100, lo: 0x84, hi: 0x84}, + {value: 0x8800, lo: 0x88, hi: 0x88}, + {value: 0x1100, lo: 0x89, hi: 0x89}, + {value: 0x8800, lo: 0x8b, hi: 0x8b}, + {value: 0x1100, lo: 0x8c, hi: 0x8c}, + {value: 0x8800, lo: 0xa3, hi: 0xa3}, + {value: 0x1100, lo: 0xa4, hi: 0xa5}, + {value: 0x1100, lo: 0xa6, hi: 0xa6}, + {value: 0x3000, lo: 0xac, hi: 0xac}, + {value: 0x3000, lo: 0xad, hi: 0xad}, + {value: 0x3000, lo: 0xaf, hi: 0xaf}, + {value: 0x3000, lo: 0xb0, hi: 0xb0}, + {value: 0x8800, lo: 0xbc, hi: 0xbc}, + // Block 0x53, offset 0x54 + {value: 0x0000, lo: 0x0b}, + {value: 0x1100, lo: 0x80, hi: 0x81}, + {value: 0x8800, lo: 0x82, hi: 0x83}, + {value: 0x1100, lo: 0x84, hi: 0x85}, + {value: 0x8800, lo: 0x86, hi: 0x87}, + {value: 0x1100, lo: 0x88, hi: 0x89}, + {value: 0x8800, lo: 0x91, hi: 0x92}, + {value: 0x8800, lo: 0xa2, hi: 0xa2}, + {value: 0x8800, lo: 0xa8, hi: 0xa9}, + {value: 0x8800, lo: 0xab, hi: 0xab}, + {value: 0x1100, lo: 0xac, hi: 0xaf}, + {value: 0x8800, lo: 0xb2, hi: 0xb5}, + // Block 0x54, offset 0x55 + {value: 0x0000, lo: 0x02}, + {value: 0x1100, lo: 0xa0, hi: 0xa3}, + {value: 0x1100, lo: 0xaa, hi: 0xad}, + // Block 0x55, offset 0x56 + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0xa9, hi: 0xaa}, + // Block 0x56, offset 0x57 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xa0, hi: 0xbf}, + // Block 0x57, offset 0x58 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xbf}, + // Block 0x58, offset 0x59 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xaa}, + // Block 0x59, offset 0x5a + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x8c, hi: 0x8c}, + // Block 0x5a, offset 0x5b + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xb4, hi: 0xb6}, + // Block 0x5b, offset 0x5c + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0x9c, hi: 0x9c}, + // Block 0x5c, offset 0x5d + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xbc, hi: 0xbd}, + // Block 0x5d, offset 0x5e + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0xaf, hi: 0xb1}, + // Block 0x5e, offset 0x5f + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0xaf, hi: 0xaf}, + {value: 0x0009, lo: 0xbf, hi: 0xbf}, + // Block 0x5f, offset 0x60 + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0xa0, hi: 0xbf}, + // Block 0x60, offset 0x61 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x9f, hi: 0x9f}, + // Block 0x61, offset 0x62 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xb3, hi: 0xb3}, + // Block 0x62, offset 0x63 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0x95}, + // Block 0x63, offset 0x64 + {value: 0x0000, lo: 0x08}, + {value: 0x3000, lo: 0x80, hi: 0x80}, + {value: 0x00da, lo: 0xaa, hi: 0xaa}, + {value: 0x00e4, lo: 0xab, hi: 0xab}, + {value: 0x00e8, lo: 0xac, hi: 0xac}, + {value: 0x00de, lo: 0xad, hi: 0xad}, + {value: 0x00e0, lo: 0xae, hi: 0xaf}, + {value: 0x3000, lo: 0xb6, hi: 0xb6}, + {value: 0x3000, lo: 0xb8, hi: 0xba}, + // Block 0x64, offset 0x65 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xb1, hi: 0xbf}, + // Block 0x65, offset 0x66 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x8e}, + {value: 0x3000, lo: 0x92, hi: 0x9f}, + // Block 0x66, offset 0x67 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x9e}, + {value: 0x3000, lo: 0xa0, hi: 0xbf}, + // Block 0x67, offset 0x68 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x87}, + {value: 0x3000, lo: 0x90, hi: 0xbe}, + // Block 0x68, offset 0x69 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xbe}, + // Block 0x69, offset 0x6a + {value: 0x0000, lo: 0x02}, + {value: 0x00e6, lo: 0xaf, hi: 0xaf}, + {value: 0x00e6, lo: 0xbc, hi: 0xbd}, + // Block 0x6a, offset 0x6b + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0xb0, hi: 0xb1}, + // Block 0x6b, offset 0x6c + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xb0, hi: 0xb0}, + // Block 0x6c, offset 0x6d + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x86, hi: 0x86}, + // Block 0x6d, offset 0x6e + {value: 0x0000, lo: 0x02}, + {value: 0x0009, lo: 0x84, hi: 0x84}, + {value: 0x00e6, lo: 0xa0, hi: 0xb1}, + // Block 0x6e, offset 0x6f + {value: 0x0000, lo: 0x01}, + {value: 0x00dc, lo: 0xab, hi: 0xad}, + // Block 0x6f, offset 0x70 + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x93, hi: 0x93}, + // Block 0x70, offset 0x71 + {value: 0x0000, lo: 0x01}, + {value: 0x0007, lo: 0xb3, hi: 0xb3}, + // Block 0x71, offset 0x72 + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x80, hi: 0x80}, + // Block 0x72, offset 0x73 + {value: 0x0000, lo: 0x05}, + {value: 0x00e6, lo: 0xb0, hi: 0xb0}, + {value: 0x00e6, lo: 0xb2, hi: 0xb3}, + {value: 0x00dc, lo: 0xb4, hi: 0xb4}, + {value: 0x00e6, lo: 0xb7, hi: 0xb8}, + {value: 0x00e6, lo: 0xbe, hi: 0xbf}, + // Block 0x73, offset 0x74 + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0x81, hi: 0x81}, + // Block 0x74, offset 0x75 + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0xad, hi: 0xad}, + // Block 0x75, offset 0x76 + {value: 0x0000, lo: 0x01}, + {value: 0x1100, lo: 0x80, hi: 0xbf}, + // Block 0x76, offset 0x77 + {value: 0x0000, lo: 0x01}, + {value: 0x1100, lo: 0x80, hi: 0xa3}, + // Block 0x77, offset 0x78 + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0x80, hi: 0xbf}, + // Block 0x78, offset 0x79 + {value: 0x0000, lo: 0x09}, + {value: 0x3300, lo: 0x80, hi: 0x8d}, + {value: 0x3300, lo: 0x90, hi: 0x90}, + {value: 0x3300, lo: 0x92, hi: 0x92}, + {value: 0x3300, lo: 0x95, hi: 0x9e}, + {value: 0x3300, lo: 0xa0, hi: 0xa0}, + {value: 0x3300, lo: 0xa2, hi: 0xa2}, + {value: 0x3300, lo: 0xa5, hi: 0xa6}, + {value: 0x3300, lo: 0xaa, hi: 0xad}, + {value: 0x3300, lo: 0xb0, hi: 0xbf}, + // Block 0x79, offset 0x7a + {value: 0x0000, lo: 0x02}, + {value: 0x3300, lo: 0x80, hi: 0xad}, + {value: 0x3300, lo: 0xb0, hi: 0xbf}, + // Block 0x7a, offset 0x7b + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0x80, hi: 0x99}, + // Block 0x7b, offset 0x7c + {value: 0x0000, lo: 0x09}, + {value: 0x3000, lo: 0x80, hi: 0x86}, + {value: 0x3000, lo: 0x93, hi: 0x97}, + {value: 0x3300, lo: 0x9d, hi: 0x9d}, + {value: 0x001a, lo: 0x9e, hi: 0x9e}, + {value: 0x3300, lo: 0x9f, hi: 0x9f}, + {value: 0x3000, lo: 0xa0, hi: 0xa9}, + {value: 0x3300, lo: 0xaa, hi: 0xb6}, + {value: 0x3300, lo: 0xb8, hi: 0xbc}, + {value: 0x3300, lo: 0xbe, hi: 0xbe}, + // Block 0x7c, offset 0x7d + {value: 0x0000, lo: 0x04}, + {value: 0x3300, lo: 0x80, hi: 0x81}, + {value: 0x3300, lo: 0x83, hi: 0x84}, + {value: 0x3300, lo: 0x86, hi: 0x8e}, + {value: 0x3000, lo: 0x8f, hi: 0xbf}, + // Block 0x7d, offset 0x7e + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xb1}, + // Block 0x7e, offset 0x7f + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x93, hi: 0xbf}, + // Block 0x7f, offset 0x80 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xbd}, + // Block 0x80, offset 0x81 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x90, hi: 0xbf}, + // Block 0x81, offset 0x82 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x8f}, + {value: 0x3000, lo: 0x92, hi: 0xbf}, + // Block 0x82, offset 0x83 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x87}, + {value: 0x3000, lo: 0xb0, hi: 0xbc}, + // Block 0x83, offset 0x84 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0x90, hi: 0x99}, + {value: 0x00e6, lo: 0xa0, hi: 0xa6}, + {value: 0x3000, lo: 0xb0, hi: 0xbf}, + // Block 0x84, offset 0x85 + {value: 0x0000, lo: 0x07}, + {value: 0x3000, lo: 0x80, hi: 0x84}, + {value: 0x3000, lo: 0x87, hi: 0x92}, + {value: 0x3000, lo: 0x94, hi: 0xa6}, + {value: 0x3000, lo: 0xa8, hi: 0xab}, + {value: 0x3000, lo: 0xb0, hi: 0xb2}, + {value: 0x3000, lo: 0xb4, hi: 0xb4}, + {value: 0x3000, lo: 0xb6, hi: 0xbf}, + // Block 0x85, offset 0x86 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xbc}, + // Block 0x86, offset 0x87 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x81, hi: 0xbf}, + // Block 0x87, offset 0x88 + {value: 0x0000, lo: 0x06}, + {value: 0x3000, lo: 0x82, hi: 0x87}, + {value: 0x3000, lo: 0x8a, hi: 0x8f}, + {value: 0x3000, lo: 0x92, hi: 0x97}, + {value: 0x3000, lo: 0x9a, hi: 0x9c}, + {value: 0x3000, lo: 0xa0, hi: 0xa6}, + {value: 0x3000, lo: 0xa8, hi: 0xae}, + // Block 0x88, offset 0x89 + {value: 0x0000, lo: 0x01}, + {value: 0x00dc, lo: 0xbd, hi: 0xbd}, + // Block 0x89, offset 0x8a + {value: 0x0000, lo: 0x06}, + {value: 0x00dc, lo: 0x8d, hi: 0x8d}, + {value: 0x00e6, lo: 0x8f, hi: 0x8f}, + {value: 0x00e6, lo: 0xb8, hi: 0xb8}, + {value: 0x0001, lo: 0xb9, hi: 0xb9}, + {value: 0x00dc, lo: 0xba, hi: 0xba}, + {value: 0x0009, lo: 0xbf, hi: 0xbf}, + // Block 0x8a, offset 0x8b + {value: 0x7700, lo: 0x07}, + {value: 0x8800, lo: 0x99, hi: 0x99}, + {value: 0x1100, lo: 0x9a, hi: 0x9b}, + {value: 0x1100, lo: 0x9c, hi: 0x9c}, + {value: 0x8800, lo: 0xa5, hi: 0xa5}, + {value: 0x1100, lo: 0xab, hi: 0xab}, + {value: 0x0009, lo: 0xb9, hi: 0xb9}, + {value: 0x6607, lo: 0xba, hi: 0xba}, + // Block 0x8b, offset 0x8c + {value: 0x0000, lo: 0x06}, + {value: 0x3300, lo: 0x9e, hi: 0xa4}, + {value: 0x00d8, lo: 0xa5, hi: 0xa6}, + {value: 0x0001, lo: 0xa7, hi: 0xa9}, + {value: 0x00e2, lo: 0xad, hi: 0xad}, + {value: 0x00d8, lo: 0xae, hi: 0xb2}, + {value: 0x00dc, lo: 0xbb, hi: 0xbf}, + // Block 0x8c, offset 0x8d + {value: 0x0000, lo: 0x05}, + {value: 0x00dc, lo: 0x80, hi: 0x82}, + {value: 0x00e6, lo: 0x85, hi: 0x89}, + {value: 0x00dc, lo: 0x8a, hi: 0x8b}, + {value: 0x00e6, lo: 0xaa, hi: 0xad}, + {value: 0x3300, lo: 0xbb, hi: 0xbf}, + // Block 0x8d, offset 0x8e + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0x80, hi: 0x80}, + // Block 0x8e, offset 0x8f + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0x82, hi: 0x84}, + // Block 0x8f, offset 0x90 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x94}, + {value: 0x3000, lo: 0x96, hi: 0xbf}, + // Block 0x90, offset 0x91 + {value: 0x0000, lo: 0x08}, + {value: 0x3000, lo: 0x80, hi: 0x9c}, + {value: 0x3000, lo: 0x9e, hi: 0x9f}, + {value: 0x3000, lo: 0xa2, hi: 0xa2}, + {value: 0x3000, lo: 0xa5, hi: 0xa6}, + {value: 0x3000, lo: 0xa9, hi: 0xac}, + {value: 0x3000, lo: 0xae, hi: 0xb9}, + {value: 0x3000, lo: 0xbb, hi: 0xbb}, + {value: 0x3000, lo: 0xbd, hi: 0xbf}, + // Block 0x91, offset 0x92 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x83}, + {value: 0x3000, lo: 0x85, hi: 0xbf}, + // Block 0x92, offset 0x93 + {value: 0x0000, lo: 0x06}, + {value: 0x3000, lo: 0x80, hi: 0x85}, + {value: 0x3000, lo: 0x87, hi: 0x8a}, + {value: 0x3000, lo: 0x8d, hi: 0x94}, + {value: 0x3000, lo: 0x96, hi: 0x9c}, + {value: 0x3000, lo: 0x9e, hi: 0xb9}, + {value: 0x3000, lo: 0xbb, hi: 0xbe}, + // Block 0x93, offset 0x94 + {value: 0x0000, lo: 0x04}, + {value: 0x3000, lo: 0x80, hi: 0x84}, + {value: 0x3000, lo: 0x86, hi: 0x86}, + {value: 0x3000, lo: 0x8a, hi: 0x90}, + {value: 0x3000, lo: 0x92, hi: 0xbf}, + // Block 0x94, offset 0x95 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0xa5}, + {value: 0x3000, lo: 0xa8, hi: 0xbf}, + // Block 0x95, offset 0x96 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x8b}, + {value: 0x3000, lo: 0x8e, hi: 0xbf}, + // Block 0x96, offset 0x97 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0x80, hi: 0x8a}, + {value: 0x3000, lo: 0x90, hi: 0xae}, + {value: 0x3000, lo: 0xb0, hi: 0xbf}, + // Block 0x97, offset 0x98 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0x8f}, + // Block 0x98, offset 0x99 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x90, hi: 0x90}, + // Block 0x99, offset 0x9a + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x82}, + {value: 0x3000, lo: 0x90, hi: 0xba}, + // Block 0x9a, offset 0x9b + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x88}, + {value: 0x3000, lo: 0x90, hi: 0x91}, + // Block 0x9b, offset 0x9c + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0x80, hi: 0x9d}, } // charInfoLookup: 1152 bytes @@ -6498,83 +6689,83 @@ var charInfoLookup = [1152]uint8{ // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x0c2: 0x03, 0x0c3: 0x04, 0x0c4: 0x05, 0x0c5: 0x06, 0x0c6: 0x07, 0x0c7: 0x08, - 0x0c8: 0x09, 0x0ca: 0x0a, 0x0cb: 0x0b, 0x0cc: 0x0c, 0x0cd: 0x0d, 0x0ce: 0x0e, 0x0cf: 0x0f, - 0x0d0: 0x10, 0x0d1: 0x11, 0x0d2: 0x12, 0x0d3: 0x13, 0x0d6: 0x14, 0x0d7: 0x15, - 0x0d8: 0x16, 0x0d9: 0x17, 0x0db: 0x18, 0x0dc: 0x19, 0x0dd: 0x1a, 0x0df: 0x1b, + 0x0c2: 0x10, 0x0c3: 0x03, 0x0c4: 0x11, 0x0c5: 0x12, 0x0c6: 0x13, 0x0c7: 0x14, + 0x0c8: 0x15, 0x0ca: 0x16, 0x0cb: 0x17, 0x0cc: 0x04, 0x0cd: 0x05, 0x0ce: 0x06, 0x0cf: 0x18, + 0x0d0: 0x07, 0x0d1: 0x19, 0x0d2: 0x1a, 0x0d3: 0x1b, 0x0d6: 0x08, 0x0d7: 0x1c, + 0x0d8: 0x1d, 0x0d9: 0x09, 0x0db: 0x1e, 0x0dc: 0x1f, 0x0dd: 0x20, 0x0df: 0x21, 0x0e0: 0x04, 0x0e1: 0x05, 0x0e2: 0x06, 0x0e3: 0x07, 0x0ea: 0x08, 0x0eb: 0x09, 0x0ec: 0x09, 0x0ed: 0x0a, 0x0ef: 0x0b, 0x0f0: 0x11, // Block 0x4, offset 0x100 - 0x120: 0x1c, 0x121: 0x1d, 0x124: 0x1e, 0x125: 0x1f, 0x126: 0x20, 0x127: 0x21, - 0x128: 0x22, 0x129: 0x23, 0x12a: 0x24, 0x12b: 0x25, 0x12c: 0x20, 0x12d: 0x26, 0x12e: 0x27, 0x12f: 0x28, - 0x131: 0x29, 0x132: 0x2a, 0x133: 0x2b, 0x134: 0x2c, 0x135: 0x28, 0x137: 0x2d, - 0x138: 0x2e, 0x139: 0x2f, 0x13a: 0x30, 0x13b: 0x31, 0x13c: 0x32, 0x13d: 0x33, 0x13e: 0x34, 0x13f: 0x35, + 0x120: 0x22, 0x121: 0x23, 0x124: 0x24, 0x125: 0x25, 0x126: 0x26, 0x127: 0x27, + 0x128: 0x28, 0x129: 0x29, 0x12a: 0x2a, 0x12b: 0x2b, 0x12c: 0x26, 0x12d: 0x2c, 0x12e: 0x2d, 0x12f: 0x2e, + 0x131: 0x2f, 0x132: 0x30, 0x133: 0x31, 0x134: 0x32, 0x135: 0x2e, 0x137: 0x33, + 0x138: 0x34, 0x139: 0x35, 0x13a: 0x36, 0x13b: 0x37, 0x13c: 0x38, 0x13d: 0x39, 0x13e: 0x3a, 0x13f: 0x3b, // Block 0x5, offset 0x140 - 0x140: 0x36, 0x142: 0x37, 0x143: 0x38, 0x145: 0x39, 0x146: 0x3a, 0x147: 0x3b, - 0x14d: 0x3c, - 0x15c: 0x3d, 0x15f: 0x3e, - 0x162: 0x3f, 0x164: 0x40, - 0x168: 0x41, 0x169: 0x42, 0x16c: 0x43, 0x16d: 0x44, 0x16e: 0x45, 0x16f: 0x46, - 0x170: 0x47, 0x173: 0x48, 0x174: 0x49, 0x175: 0x4a, 0x176: 0x4b, 0x177: 0x4c, - 0x178: 0x4d, 0x179: 0x4e, 0x17a: 0x4f, 0x17b: 0x50, 0x17c: 0x51, 0x17d: 0x52, 0x17e: 0x53, 0x17f: 0x54, + 0x140: 0x3c, 0x142: 0x3d, 0x143: 0x3e, 0x144: 0x3f, 0x145: 0x40, 0x146: 0x41, 0x147: 0x42, + 0x14d: 0x43, + 0x15c: 0x44, 0x15f: 0x45, + 0x162: 0x46, 0x164: 0x47, + 0x168: 0x48, 0x169: 0x49, 0x16c: 0x4a, 0x16d: 0x4b, 0x16e: 0x4c, 0x16f: 0x4d, + 0x170: 0x4e, 0x173: 0x4f, 0x174: 0x50, 0x175: 0x51, 0x176: 0x52, 0x177: 0x53, + 0x178: 0x54, 0x179: 0x55, 0x17a: 0x56, 0x17b: 0x57, 0x17c: 0x58, 0x17d: 0x0a, 0x17e: 0x59, 0x17f: 0x0b, // Block 0x6, offset 0x180 - 0x180: 0x55, 0x181: 0x56, 0x182: 0x57, 0x183: 0x58, 0x184: 0x59, 0x185: 0x5a, 0x186: 0x5b, 0x187: 0x5c, - 0x188: 0x5d, 0x189: 0x5e, 0x18a: 0x5f, 0x18b: 0x60, 0x18c: 0x61, - 0x191: 0x62, 0x192: 0x63, 0x193: 0x64, - 0x1a8: 0x65, 0x1a9: 0x66, 0x1ab: 0x67, - 0x1b1: 0x68, 0x1b3: 0x69, 0x1b5: 0x6a, 0x1b7: 0x6b, - 0x1ba: 0x6c, 0x1bb: 0x6d, 0x1bc: 0x63, 0x1bd: 0x63, 0x1be: 0x63, 0x1bf: 0x6e, + 0x180: 0x5a, 0x181: 0x5b, 0x182: 0x5c, 0x183: 0x5d, 0x184: 0x5e, 0x185: 0x5f, 0x186: 0x60, 0x187: 0x61, + 0x188: 0x62, 0x189: 0x0c, 0x18a: 0x63, 0x18b: 0x64, 0x18c: 0x65, + 0x191: 0x66, 0x192: 0x67, 0x193: 0x68, + 0x1a8: 0x69, 0x1a9: 0x6a, 0x1ab: 0x6b, + 0x1b1: 0x6c, 0x1b3: 0x6d, 0x1b5: 0x6e, 0x1b7: 0x6f, + 0x1ba: 0x70, 0x1bb: 0x71, 0x1bc: 0x67, 0x1bd: 0x67, 0x1be: 0x67, 0x1bf: 0x72, // Block 0x7, offset 0x1c0 - 0x1c0: 0x6f, 0x1c1: 0x70, 0x1c2: 0x71, 0x1c3: 0x72, 0x1c4: 0x73, 0x1c5: 0x63, 0x1c6: 0x74, - 0x1c8: 0x75, 0x1c9: 0x76, 0x1ca: 0x63, 0x1cb: 0x77, 0x1cc: 0x63, 0x1cd: 0x63, 0x1ce: 0x63, 0x1cf: 0x63, + 0x1c0: 0x73, 0x1c1: 0x0d, 0x1c2: 0x0e, 0x1c3: 0x0f, 0x1c4: 0x74, 0x1c5: 0x67, 0x1c6: 0x75, + 0x1c8: 0x76, 0x1c9: 0x77, 0x1ca: 0x67, 0x1cb: 0x78, 0x1cc: 0x67, 0x1cd: 0x67, 0x1ce: 0x67, 0x1cf: 0x67, // Block 0x8, offset 0x200 - 0x219: 0x78, 0x21b: 0x79, 0x21d: 0x7a, - 0x220: 0x7b, 0x223: 0x7c, 0x224: 0x7d, 0x225: 0x7e, 0x226: 0x7f, 0x227: 0x80, - 0x22a: 0x81, 0x22b: 0x82, 0x22f: 0x83, - 0x230: 0x84, 0x231: 0x84, 0x232: 0x84, 0x233: 0x84, 0x234: 0x84, 0x235: 0x84, 0x236: 0x84, 0x237: 0x84, - 0x238: 0x84, 0x239: 0x84, 0x23a: 0x84, 0x23b: 0x84, 0x23c: 0x84, 0x23d: 0x84, 0x23e: 0x84, 0x23f: 0x84, + 0x219: 0x79, 0x21b: 0x7a, 0x21d: 0x7b, + 0x220: 0x7c, 0x223: 0x7d, 0x224: 0x7e, 0x225: 0x7f, 0x226: 0x80, 0x227: 0x81, + 0x22a: 0x82, 0x22b: 0x83, 0x22f: 0x84, + 0x230: 0x85, 0x231: 0x85, 0x232: 0x85, 0x233: 0x85, 0x234: 0x85, 0x235: 0x85, 0x236: 0x85, 0x237: 0x85, + 0x238: 0x85, 0x239: 0x85, 0x23a: 0x85, 0x23b: 0x85, 0x23c: 0x85, 0x23d: 0x85, 0x23e: 0x85, 0x23f: 0x85, // Block 0x9, offset 0x240 - 0x240: 0x84, 0x241: 0x84, 0x242: 0x84, 0x243: 0x84, 0x244: 0x84, 0x245: 0x84, 0x246: 0x84, 0x247: 0x84, - 0x248: 0x84, 0x249: 0x84, 0x24a: 0x84, 0x24b: 0x84, 0x24c: 0x84, 0x24d: 0x84, 0x24e: 0x84, 0x24f: 0x84, - 0x250: 0x84, 0x251: 0x84, 0x252: 0x84, 0x253: 0x84, 0x254: 0x84, 0x255: 0x84, 0x256: 0x84, 0x257: 0x84, - 0x258: 0x84, 0x259: 0x84, 0x25a: 0x84, 0x25b: 0x84, 0x25c: 0x84, 0x25d: 0x84, 0x25e: 0x84, 0x25f: 0x84, - 0x260: 0x84, 0x261: 0x84, 0x262: 0x84, 0x263: 0x84, 0x264: 0x84, 0x265: 0x84, 0x266: 0x84, 0x267: 0x84, - 0x268: 0x84, 0x269: 0x84, 0x26a: 0x84, 0x26b: 0x84, 0x26c: 0x84, 0x26d: 0x84, 0x26e: 0x84, 0x26f: 0x84, - 0x270: 0x84, 0x271: 0x84, 0x272: 0x84, 0x273: 0x84, 0x274: 0x84, 0x275: 0x84, 0x276: 0x84, 0x277: 0x84, - 0x278: 0x84, 0x279: 0x84, 0x27a: 0x84, 0x27b: 0x84, 0x27c: 0x84, 0x27d: 0x84, 0x27e: 0x84, 0x27f: 0x84, + 0x240: 0x85, 0x241: 0x85, 0x242: 0x85, 0x243: 0x85, 0x244: 0x85, 0x245: 0x85, 0x246: 0x85, 0x247: 0x85, + 0x248: 0x85, 0x249: 0x85, 0x24a: 0x85, 0x24b: 0x85, 0x24c: 0x85, 0x24d: 0x85, 0x24e: 0x85, 0x24f: 0x85, + 0x250: 0x85, 0x251: 0x85, 0x252: 0x85, 0x253: 0x85, 0x254: 0x85, 0x255: 0x85, 0x256: 0x85, 0x257: 0x85, + 0x258: 0x85, 0x259: 0x85, 0x25a: 0x85, 0x25b: 0x85, 0x25c: 0x85, 0x25d: 0x85, 0x25e: 0x85, 0x25f: 0x85, + 0x260: 0x85, 0x261: 0x85, 0x262: 0x85, 0x263: 0x85, 0x264: 0x85, 0x265: 0x85, 0x266: 0x85, 0x267: 0x85, + 0x268: 0x85, 0x269: 0x85, 0x26a: 0x85, 0x26b: 0x85, 0x26c: 0x85, 0x26d: 0x85, 0x26e: 0x85, 0x26f: 0x85, + 0x270: 0x85, 0x271: 0x85, 0x272: 0x85, 0x273: 0x85, 0x274: 0x85, 0x275: 0x85, 0x276: 0x85, 0x277: 0x85, + 0x278: 0x85, 0x279: 0x85, 0x27a: 0x85, 0x27b: 0x85, 0x27c: 0x85, 0x27d: 0x85, 0x27e: 0x85, 0x27f: 0x85, // Block 0xa, offset 0x280 - 0x280: 0x84, 0x281: 0x84, 0x282: 0x84, 0x283: 0x84, 0x284: 0x84, 0x285: 0x84, 0x286: 0x84, 0x287: 0x84, - 0x288: 0x84, 0x289: 0x84, 0x28a: 0x84, 0x28b: 0x84, 0x28c: 0x84, 0x28d: 0x84, 0x28e: 0x84, 0x28f: 0x84, - 0x290: 0x84, 0x291: 0x84, 0x292: 0x84, 0x293: 0x84, 0x294: 0x84, 0x295: 0x84, 0x296: 0x84, 0x297: 0x84, - 0x298: 0x84, 0x299: 0x84, 0x29a: 0x84, 0x29b: 0x84, 0x29c: 0x84, 0x29d: 0x84, 0x29e: 0x85, + 0x280: 0x85, 0x281: 0x85, 0x282: 0x85, 0x283: 0x85, 0x284: 0x85, 0x285: 0x85, 0x286: 0x85, 0x287: 0x85, + 0x288: 0x85, 0x289: 0x85, 0x28a: 0x85, 0x28b: 0x85, 0x28c: 0x85, 0x28d: 0x85, 0x28e: 0x85, 0x28f: 0x85, + 0x290: 0x85, 0x291: 0x85, 0x292: 0x85, 0x293: 0x85, 0x294: 0x85, 0x295: 0x85, 0x296: 0x85, 0x297: 0x85, + 0x298: 0x85, 0x299: 0x85, 0x29a: 0x85, 0x29b: 0x85, 0x29c: 0x85, 0x29d: 0x85, 0x29e: 0x86, // Block 0xb, offset 0x2c0 - 0x2e4: 0x86, 0x2e5: 0x86, 0x2e6: 0x86, 0x2e7: 0x86, - 0x2e8: 0x87, 0x2e9: 0x88, 0x2ea: 0x86, 0x2eb: 0x89, 0x2ec: 0x8a, 0x2ed: 0x8b, 0x2ee: 0x8c, 0x2ef: 0x8d, - 0x2f0: 0x63, 0x2f1: 0x63, 0x2f2: 0x63, 0x2f3: 0x63, 0x2f4: 0x8e, 0x2f5: 0x8f, 0x2f6: 0x90, 0x2f7: 0x91, - 0x2f8: 0x92, 0x2f9: 0x93, 0x2fa: 0x63, 0x2fb: 0x94, 0x2fc: 0x95, 0x2fd: 0x63, 0x2fe: 0x77, 0x2ff: 0x96, + 0x2e4: 0x87, 0x2e5: 0x87, 0x2e6: 0x87, 0x2e7: 0x87, + 0x2e8: 0x88, 0x2e9: 0x89, 0x2ea: 0x87, 0x2eb: 0x8a, 0x2ec: 0x8b, 0x2ed: 0x8c, 0x2ee: 0x8d, 0x2ef: 0x8e, + 0x2f0: 0x67, 0x2f1: 0x67, 0x2f2: 0x67, 0x2f3: 0x67, 0x2f4: 0x8f, 0x2f5: 0x90, 0x2f6: 0x91, 0x2f7: 0x92, + 0x2f8: 0x93, 0x2f9: 0x94, 0x2fa: 0x67, 0x2fb: 0x95, 0x2fc: 0x96, 0x2fd: 0x67, 0x2fe: 0x78, 0x2ff: 0x97, // Block 0xc, offset 0x300 - 0x307: 0x97, - 0x328: 0x98, + 0x307: 0x98, + 0x328: 0x99, // Block 0xd, offset 0x340 - 0x341: 0x7b, 0x342: 0x99, + 0x341: 0x7c, 0x342: 0x9a, // Block 0xe, offset 0x380 - 0x385: 0x9a, 0x386: 0x9b, 0x387: 0x9c, - 0x389: 0x9d, - 0x390: 0x63, 0x391: 0x9e, 0x392: 0x9f, 0x393: 0xa0, 0x394: 0xa1, 0x395: 0xa2, 0x396: 0x63, 0x397: 0x63, - 0x398: 0x63, 0x399: 0x63, 0x39a: 0xa3, 0x39b: 0x63, 0x39c: 0x63, 0x39d: 0x63, 0x39e: 0x63, 0x39f: 0xa4, + 0x385: 0x9b, 0x386: 0x9c, 0x387: 0x9d, + 0x389: 0x9e, + 0x390: 0x67, 0x391: 0x9f, 0x392: 0xa0, 0x393: 0xa1, 0x394: 0xa2, 0x395: 0xa3, 0x396: 0x67, 0x397: 0x67, + 0x398: 0x67, 0x399: 0x67, 0x39a: 0xa4, 0x39b: 0x67, 0x39c: 0x67, 0x39d: 0x67, 0x39e: 0x67, 0x39f: 0xa5, // Block 0xf, offset 0x3c0 - 0x3c4: 0xa5, 0x3c5: 0xa6, 0x3c6: 0xa7, - 0x3c8: 0xa8, 0x3c9: 0xa9, + 0x3c4: 0xa6, 0x3c5: 0xa7, 0x3c6: 0xa8, + 0x3c8: 0xa9, 0x3c9: 0xaa, // Block 0x10, offset 0x400 - 0x420: 0x86, 0x421: 0x86, 0x422: 0x86, 0x423: 0x86, 0x424: 0x86, 0x425: 0x86, 0x426: 0x86, 0x427: 0x86, - 0x428: 0xaa, + 0x420: 0x87, 0x421: 0x87, 0x422: 0x87, 0x423: 0x87, 0x424: 0x87, 0x425: 0x87, 0x426: 0x87, 0x427: 0x87, + 0x428: 0xab, // Block 0x11, offset 0x440 0x450: 0x0c, 0x451: 0x0d, 0x45d: 0x0e, 0x45f: 0x0f, 0x46f: 0x10, } -var charInfoTrie = trie{charInfoLookup[:], charInfoValues[:]} +var charInfoTrie = trie{charInfoLookup[:], charInfoValues[:], charInfoSparseValues[:], charInfoSparseOffset[:], 16} -// Total size of tables: 78KB (80234 bytes) +// Total size of tables: 48KB (48756 bytes) diff --git a/libgo/go/exp/norm/trie.go b/libgo/go/exp/norm/trie.go index 6b654018757..93cb9c33903 100644 --- a/libgo/go/exp/norm/trie.go +++ b/libgo/go/exp/norm/trie.go @@ -4,9 +4,44 @@ package norm +type valueRange struct { + value uint16 // header: value:stride + lo, hi byte // header: lo:n +} + type trie struct { - index []uint8 - values []uint16 + index []uint8 + values []uint16 + sparse []valueRange + sparseOffset []uint16 + cutoff uint8 // indices >= cutoff are sparse +} + +// lookupValue determines the type of block n and looks up the value for b. +// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block +// is a list of ranges with an accompanying value. Given a matching range r, +// the value for b is by r.value + (b - r.lo) * stride. +func (t *trie) lookupValue(n uint8, b byte) uint16 { + if n < t.cutoff { + return t.values[uint16(n)<<6+uint16(b&maskx)] + } + offset := t.sparseOffset[n-t.cutoff] + header := t.sparse[offset] + lo := offset + 1 + hi := lo + uint16(header.lo) + for lo < hi { + m := lo + (hi-lo)/2 + r := t.sparse[m] + if r.lo <= b && b <= r.hi { + return r.value + uint16(b-r.lo)*header.value + } + if b < r.lo { + hi = m + } else { + lo = m + 1 + } + } + return 0 } const ( @@ -44,8 +79,7 @@ func (t *trie) lookup(s []byte) (v uint16, sz int) { if c1 < tx || t2 <= c1 { return 0, 1 } - o := uint16(i)<<6 + uint16(c1)&maskx - return t.values[o], 2 + return t.lookupValue(i, c1), 2 case c0 < t4: if len(s) < 3 { return 0, 0 @@ -61,8 +95,7 @@ func (t *trie) lookup(s []byte) (v uint16, sz int) { if c2 < tx || t2 <= c2 { return 0, 2 } - o = uint16(i)<<6 + uint16(c2)&maskx - return t.values[o], 3 + return t.lookupValue(i, c2), 3 case c0 < t5: if len(s) < 4 { return 0, 0 @@ -84,18 +117,7 @@ func (t *trie) lookup(s []byte) (v uint16, sz int) { if c3 < tx || t2 <= c3 { return 0, 3 } - o = uint16(i)<<6 + uint16(c3)&maskx - return t.values[o], 4 - case c0 < t6: - if len(s) < 5 { - return 0, 0 - } - return 0, 5 - case c0 < te: - if len(s) < 6 { - return 0, 0 - } - return 0, 6 + return t.lookupValue(i, c3), 4 } // Illegal rune return 0, 1 @@ -120,8 +142,7 @@ func (t *trie) lookupString(s string) (v uint16, sz int) { if c1 < tx || t2 <= c1 { return 0, 1 } - o := uint16(i)<<6 + uint16(c1)&maskx - return t.values[o], 2 + return t.lookupValue(i, c1), 2 case c0 < t4: if len(s) < 3 { return 0, 0 @@ -137,8 +158,7 @@ func (t *trie) lookupString(s string) (v uint16, sz int) { if c2 < tx || t2 <= c2 { return 0, 2 } - o = uint16(i)<<6 + uint16(c2)&maskx - return t.values[o], 3 + return t.lookupValue(i, c2), 3 case c0 < t5: if len(s) < 4 { return 0, 0 @@ -160,18 +180,7 @@ func (t *trie) lookupString(s string) (v uint16, sz int) { if c3 < tx || t2 <= c3 { return 0, 3 } - o = uint16(i)<<6 + uint16(c3)&maskx - return t.values[o], 4 - case c0 < t6: - if len(s) < 5 { - return 0, 0 - } - return 0, 5 - case c0 < te: - if len(s) < 6 { - return 0, 0 - } - return 0, 6 + return t.lookupValue(i, c3), 4 } // Illegal rune return 0, 1 @@ -188,19 +197,16 @@ func (t *trie) lookupUnsafe(s []byte) uint16 { return 0 } i := t.index[c0] - o := uint16(i)<<6 + uint16(s[1])&maskx if c0 < t3 { - return t.values[o] + return t.lookupValue(i, s[1]) } - i = t.index[o] - o = uint16(i)<<6 + uint16(s[2])&maskx + i = t.index[uint16(i)<<6+uint16(s[1])&maskx] if c0 < t4 { - return t.values[o] + return t.lookupValue(i, s[2]) } - i = t.index[o] - o = uint16(i)<<6 + uint16(s[3])&maskx + i = t.index[uint16(i)<<6+uint16(s[2])&maskx] if c0 < t5 { - return t.values[o] + return t.lookupValue(i, s[3]) } return 0 } @@ -216,19 +222,16 @@ func (t *trie) lookupStringUnsafe(s string) uint16 { return 0 } i := t.index[c0] - o := uint16(i)<<6 + uint16(s[1])&maskx if c0 < t3 { - return t.values[o] + return t.lookupValue(i, s[1]) } - i = t.index[o] - o = uint16(i)<<6 + uint16(s[2])&maskx + i = t.index[uint16(i)<<6+uint16(s[1])&maskx] if c0 < t4 { - return t.values[o] + return t.lookupValue(i, s[2]) } - i = t.index[o] - o = uint16(i)<<6 + uint16(s[3])&maskx + i = t.index[uint16(i)<<6+uint16(s[2])&maskx] if c0 < t5 { - return t.values[o] + return t.lookupValue(i, s[3]) } return 0 } diff --git a/libgo/go/exp/norm/trie_test.go b/libgo/go/exp/norm/trie_test.go index ad87d972b02..5649fb7eea7 100644 --- a/libgo/go/exp/norm/trie_test.go +++ b/libgo/go/exp/norm/trie_test.go @@ -8,6 +8,41 @@ import ( // Test data is located in triedata_test.go; generated by maketesttables. var testdata = testdataTrie +type rangeTest struct { + block uint8 + lookup byte + result uint16 + table []valueRange + offsets []uint16 +} + +var range1Off = []uint16{0, 2} +var range1 = []valueRange{ + {0, 1, 0}, + {1, 0x80, 0x80}, + {0, 2, 0}, + {1, 0x80, 0x80}, + {9, 0xff, 0xff}, +} + +var rangeTests = []rangeTest{ + {10, 0x80, 1, range1, range1Off}, + {10, 0x00, 0, range1, range1Off}, + {11, 0x80, 1, range1, range1Off}, + {11, 0xff, 9, range1, range1Off}, + {11, 0x00, 0, range1, range1Off}, +} + +func TestLookupSparse(t *testing.T) { + for i, test := range rangeTests { + n := trie{sparse: test.table, sparseOffset: test.offsets, cutoff: 10} + v := n.lookupValue(test.block, test.lookup) + if v != test.result { + t.Errorf("LookupSparse:%d: found %X; want %X", i, v, test.result) + } + } +} + // Test cases for illegal runes. type trietest struct { size int @@ -32,8 +67,10 @@ var tests = []trietest{ {0, []byte{t2}}, {0, []byte{t3, tx}}, {0, []byte{t4, tx, tx}}, - {0, []byte{t5, tx, tx, tx}}, - {0, []byte{t6, tx, tx, tx, tx}}, + + // we only support UTF-8 up to utf8.UTFMax bytes (4 bytes) + {1, []byte{t5, tx, tx, tx, tx}}, + {1, []byte{t6, tx, tx, tx, tx, tx}}, } func mkUtf8(rune int) ([]byte, int) { @@ -47,10 +84,10 @@ func TestLookup(t *testing.T) { b, szg := mkUtf8(tt) v, szt := testdata.lookup(b) if int(v) != i { - t.Errorf("lookup(%U): found value %#x, expected %#x", i, v, i) + t.Errorf("lookup(%U): found value %#x, expected %#x", tt, v, i) } if szt != szg { - t.Errorf("lookup(%U): found size %d, expected %d", i, szt, szg) + t.Errorf("lookup(%U): found size %d, expected %d", tt, szt, szg) } } for i, tt := range tests { diff --git a/libgo/go/exp/norm/triedata_test.go b/libgo/go/exp/norm/triedata_test.go index f886e6004a4..e8898e5d425 100644 --- a/libgo/go/exp/norm/triedata_test.go +++ b/libgo/go/exp/norm/triedata_test.go @@ -4,34 +4,55 @@ package norm -var testRunes = []int{1, 12, 127, 128, 256, 2047, 2048, 2457, 65535, 65536, 65793, 1114111} +var testRunes = []int{1, 12, 127, 128, 256, 2047, 2048, 2457, 65535, 65536, 65793, 1114111, 512, 513, 514, 528, 533} -// testdataValues: 768 entries, 1536 bytes +// testdataValues: 192 entries, 384 bytes // Block 2 is the null block. -var testdataValues = [768]uint16{ +var testdataValues = [192]uint16{ // Block 0x0, offset 0x0 0x000c: 0x0001, // Block 0x1, offset 0x40 0x007f: 0x0002, // Block 0x2, offset 0x80 - // Block 0x3, offset 0xc0 - 0x00c0: 0x0003, - // Block 0x4, offset 0x100 - 0x0100: 0x0004, - // Block 0x5, offset 0x140 - 0x017f: 0x0005, - // Block 0x6, offset 0x180 - 0x0180: 0x0006, - // Block 0x7, offset 0x1c0 - 0x01d9: 0x0007, - // Block 0x8, offset 0x200 - 0x023f: 0x0008, - // Block 0x9, offset 0x240 - 0x0240: 0x0009, - // Block 0xa, offset 0x280 - 0x0281: 0x000a, - // Block 0xb, offset 0x2c0 - 0x02ff: 0x000b, +} + +// testdataSparseOffset: 10 entries, 20 bytes +var testdataSparseOffset = []uint16{0x0, 0x2, 0x4, 0x8, 0xa, 0xc, 0xe, 0x10, 0x12, 0x14} + +// testdataSparseValues: 22 entries, 88 bytes +var testdataSparseValues = [22]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0000, lo: 0x01}, + {value: 0x0003, lo: 0x80, hi: 0x80}, + // Block 0x1, offset 0x2 + {value: 0x0000, lo: 0x01}, + {value: 0x0004, lo: 0x80, hi: 0x80}, + // Block 0x2, offset 0x3 + {value: 0x0001, lo: 0x03}, + {value: 0x000c, lo: 0x80, hi: 0x82}, + {value: 0x000f, lo: 0x90, hi: 0x90}, + {value: 0x0010, lo: 0x95, hi: 0x95}, + // Block 0x3, offset 0x4 + {value: 0x0000, lo: 0x01}, + {value: 0x0005, lo: 0xbf, hi: 0xbf}, + // Block 0x4, offset 0x5 + {value: 0x0000, lo: 0x01}, + {value: 0x0006, lo: 0x80, hi: 0x80}, + // Block 0x5, offset 0x6 + {value: 0x0000, lo: 0x01}, + {value: 0x0007, lo: 0x99, hi: 0x99}, + // Block 0x6, offset 0x7 + {value: 0x0000, lo: 0x01}, + {value: 0x0008, lo: 0xbf, hi: 0xbf}, + // Block 0x7, offset 0x8 + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x80, hi: 0x80}, + // Block 0x8, offset 0x9 + {value: 0x0000, lo: 0x01}, + {value: 0x000a, lo: 0x81, hi: 0x81}, + // Block 0x9, offset 0xa + {value: 0x0000, lo: 0x01}, + {value: 0x000b, lo: 0xbf, hi: 0xbf}, } // testdataLookup: 640 bytes @@ -42,22 +63,23 @@ var testdataLookup = [640]uint8{ // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 0x0c2: 0x03, 0x0c4: 0x04, - 0x0df: 0x05, + 0x0c8: 0x05, + 0x0df: 0x06, 0x0e0: 0x04, 0x0ef: 0x05, 0x0f0: 0x07, 0x0f4: 0x09, // Block 0x4, offset 0x100 - 0x120: 0x06, 0x126: 0x07, + 0x120: 0x07, 0x126: 0x08, // Block 0x5, offset 0x140 - 0x17f: 0x08, + 0x17f: 0x09, // Block 0x6, offset 0x180 - 0x180: 0x09, 0x184: 0x0a, + 0x180: 0x0a, 0x184: 0x0b, // Block 0x7, offset 0x1c0 0x1d0: 0x06, // Block 0x8, offset 0x200 - 0x23f: 0x0b, + 0x23f: 0x0c, // Block 0x9, offset 0x240 0x24f: 0x08, } -var testdataTrie = trie{testdataLookup[:], testdataValues[:]} +var testdataTrie = trie{testdataLookup[:], testdataValues[:], testdataSparseValues[:], testdataSparseOffset[:], 3} diff --git a/libgo/go/exp/norm/triegen.go b/libgo/go/exp/norm/triegen.go index 2b7eeee175b..515e1c7860b 100644 --- a/libgo/go/exp/norm/triegen.go +++ b/libgo/go/exp/norm/triegen.go @@ -17,10 +17,13 @@ import ( "utf8" ) +const blockSize = 64 +const maxSparseEntries = 16 + // Intermediate trie structure type trieNode struct { table [256]*trieNode - value uint16 + value int b byte leaf bool } @@ -53,6 +56,44 @@ func (n trieNode) isInternal() bool { return internal } +func (n trieNode) mostFrequentStride() int { + counts := make(map[int]int) + v := 0 + for _, t := range n.table[0x80 : 0x80+blockSize] { + if t != nil { + if stride := t.value - v; v != 0 && stride >= 0 { + counts[stride]++ + } + v = t.value + } + } + var maxs, maxc int + for stride, cnt := range counts { + if cnt > maxc { + maxs, maxc = stride, cnt + } + } + return maxs +} + +func (n trieNode) countSparseEntries() int { + stride := n.mostFrequentStride() + var count, v int + for _, t := range n.table[0x80 : 0x80+blockSize] { + tv := 0 + if t != nil { + tv = t.value + } + if tv-v != stride { + if tv != 0 { + count++ + } + } + v = tv + } + return count +} + func (n *trieNode) insert(rune int, value uint16) { var p [utf8.UTFMax]byte sz := utf8.EncodeRune(p[:], rune) @@ -69,35 +110,40 @@ func (n *trieNode) insert(rune int, value uint16) { } n = nn } - n.value = value + n.value = int(value) n.leaf = true } type nodeIndex struct { lookupBlocks []*trieNode valueBlocks []*trieNode + sparseBlocks []*trieNode + sparseOffset []uint16 + sparseCount int - lookupBlockIdx map[uint32]uint16 - valueBlockIdx map[uint32]uint16 + lookupBlockIdx map[uint32]int + valueBlockIdx map[uint32]int } func newIndex() *nodeIndex { index := &nodeIndex{} index.lookupBlocks = make([]*trieNode, 0) index.valueBlocks = make([]*trieNode, 0) - index.lookupBlockIdx = make(map[uint32]uint16) - index.valueBlockIdx = make(map[uint32]uint16) + index.sparseBlocks = make([]*trieNode, 0) + index.sparseOffset = make([]uint16, 1) + index.lookupBlockIdx = make(map[uint32]int) + index.valueBlockIdx = make(map[uint32]int) return index } -func computeOffsets(index *nodeIndex, n *trieNode) uint16 { +func computeOffsets(index *nodeIndex, n *trieNode) int { if n.leaf { return n.value } hasher := crc32.New(crc32.MakeTable(crc32.IEEE)) // We only index continuation bytes. - for i := 0; i < 64; i++ { - var v uint16 = 0 + for i := 0; i < blockSize; i++ { + v := 0 if nn := n.table[0x80+i]; nn != nil { v = computeOffsets(index, nn) } @@ -107,7 +153,7 @@ func computeOffsets(index *nodeIndex, n *trieNode) uint16 { if n.isInternal() { v, ok := index.lookupBlockIdx[h] if !ok { - v = uint16(len(index.lookupBlocks)) + v = len(index.lookupBlocks) index.lookupBlocks = append(index.lookupBlocks, n) index.lookupBlockIdx[h] = v } @@ -115,9 +161,17 @@ func computeOffsets(index *nodeIndex, n *trieNode) uint16 { } else { v, ok := index.valueBlockIdx[h] if !ok { - v = uint16(len(index.valueBlocks)) - index.valueBlocks = append(index.valueBlocks, n) - index.valueBlockIdx[h] = v + if c := n.countSparseEntries(); c > maxSparseEntries { + v = len(index.valueBlocks) + index.valueBlocks = append(index.valueBlocks, n) + index.valueBlockIdx[h] = v + } else { + v = -len(index.sparseOffset) + index.sparseBlocks = append(index.sparseBlocks, n) + index.sparseOffset = append(index.sparseOffset, uint16(index.sparseCount)) + index.sparseCount += c + 1 + index.valueBlockIdx[h] = v + } } n.value = v } @@ -125,14 +179,14 @@ func computeOffsets(index *nodeIndex, n *trieNode) uint16 { } func printValueBlock(nr int, n *trieNode, offset int) { - boff := nr * 64 + boff := nr * blockSize fmt.Printf("\n// Block %#x, offset %#x", nr, boff) var printnewline bool - for i := 0; i < 64; i++ { + for i := 0; i < blockSize; i++ { if i%6 == 0 { printnewline = true } - v := uint16(0) + v := 0 if nn := n.table[i+offset]; nn != nil { v = nn.value } @@ -141,24 +195,55 @@ func printValueBlock(nr int, n *trieNode, offset int) { fmt.Printf("\n") printnewline = false } - fmt.Printf("%#04x:%#04x, ", nr*64+i, v) + fmt.Printf("%#04x:%#04x, ", boff+i, v) } } } -func printLookupBlock(nr int, n *trieNode, offset int) { - boff := nr * 64 +func printSparseBlock(nr int, n *trieNode) { + boff := -n.value + fmt.Printf("\n// Block %#x, offset %#x", nr, boff) + v := 0 + //stride := f(n) + stride := n.mostFrequentStride() + c := n.countSparseEntries() + fmt.Printf("\n{value:%#04x,lo:%#02x},", stride, uint8(c)) + for i, nn := range n.table[0x80 : 0x80+blockSize] { + nv := 0 + if nn != nil { + nv = nn.value + } + if nv-v != stride { + if v != 0 { + fmt.Printf(",hi:%#02x},", 0x80+i-1) + } + if nv != 0 { + fmt.Printf("\n{value:%#04x,lo:%#02x", nv, nn.b) + } + } + v = nv + } + if v != 0 { + fmt.Printf(",hi:%#02x},", 0x80+blockSize-1) + } +} + +func printLookupBlock(nr int, n *trieNode, offset, cutoff int) { + boff := nr * blockSize fmt.Printf("\n// Block %#x, offset %#x", nr, boff) var printnewline bool - for i := 0; i < 64; i++ { + for i := 0; i < blockSize; i++ { if i%8 == 0 { printnewline = true } - v := uint16(0) + v := 0 if nn := n.table[i+offset]; nn != nil { v = nn.value } if v != 0 { + if v < 0 { + v = -v - 1 + cutoff + } if printnewline { fmt.Printf("\n") printnewline = false @@ -182,7 +267,7 @@ func (t *trieNode) printTables(name string) int { } } - nv := len(index.valueBlocks) * 64 + nv := len(index.valueBlocks) * blockSize fmt.Printf("// %sValues: %d entries, %d bytes\n", name, nv, nv*2) fmt.Printf("// Block 2 is the null block.\n") fmt.Printf("var %sValues = [%d]uint16 {", name, nv) @@ -194,18 +279,32 @@ func (t *trieNode) printTables(name string) int { } fmt.Print("\n}\n\n") - ni := len(index.lookupBlocks) * 64 + ls := len(index.sparseBlocks) + fmt.Printf("// %sSparseOffset: %d entries, %d bytes\n", name, ls, ls*2) + fmt.Printf("var %sSparseOffset = %#v\n\n", name, index.sparseOffset[1:]) + + ns := index.sparseCount + fmt.Printf("// %sSparseValues: %d entries, %d bytes\n", name, ns, ns*4) + fmt.Printf("var %sSparseValues = [%d]valueRange {", name, ns) + for i, n := range index.sparseBlocks { + printSparseBlock(i, n) + } + fmt.Print("\n}\n\n") + + cutoff := len(index.valueBlocks) + ni := len(index.lookupBlocks) * blockSize fmt.Printf("// %sLookup: %d bytes\n", name, ni) fmt.Printf("// Block 0 is the null block.\n") fmt.Printf("var %sLookup = [%d]uint8 {", name, ni) - printLookupBlock(0, newNode(), 0) - printLookupBlock(1, newNode(), 0) - printLookupBlock(2, newNode(), 0) - printLookupBlock(3, t, 0xC0) + printLookupBlock(0, newNode(), 0, cutoff) + printLookupBlock(1, newNode(), 0, cutoff) + printLookupBlock(2, newNode(), 0, cutoff) + printLookupBlock(3, t, 0xC0, cutoff) for i := 4; i < len(index.lookupBlocks); i++ { - printLookupBlock(i, index.lookupBlocks[i], 0x80) + printLookupBlock(i, index.lookupBlocks[i], 0x80, cutoff) } fmt.Print("\n}\n\n") - fmt.Printf("var %sTrie = trie{ %sLookup[:], %sValues[:] }\n\n", name, name, name) - return nv*2 + ni + fmt.Printf("var %sTrie = trie{ %sLookup[:], %sValues[:], %sSparseValues[:], %sSparseOffset[:], %d}\n\n", + name, name, name, name, name, cutoff) + return nv*2 + ns*4 + ni + ls*2 } diff --git a/libgo/go/exp/regexp/all_test.go b/libgo/go/exp/regexp/all_test.go deleted file mode 100644 index 77f32ca1a57..00000000000 --- a/libgo/go/exp/regexp/all_test.go +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 2009 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 regexp - -import ( - "os" - "strings" - "testing" -) - -var good_re = []string{ - ``, - `.`, - `^.$`, - `a`, - `a*`, - `a+`, - `a?`, - `a|b`, - `a*|b*`, - `(a*|b)(c*|d)`, - `[a-z]`, - `[a-abc-c\-\]\[]`, - `[a-z]+`, - `[abc]`, - `[^1234]`, - `[^\n]`, - `\!\\`, -} - -/* -type stringError struct { - re string - err os.Error -} - -var bad_re = []stringError{ - {`*`, ErrBareClosure}, - {`+`, ErrBareClosure}, - {`?`, ErrBareClosure}, - {`(abc`, ErrUnmatchedLpar}, - {`abc)`, ErrUnmatchedRpar}, - {`x[a-z`, ErrUnmatchedLbkt}, - {`abc]`, ErrUnmatchedRbkt}, - {`[z-a]`, ErrBadRange}, - {`abc\`, ErrExtraneousBackslash}, - {`a**`, ErrBadClosure}, - {`a*+`, ErrBadClosure}, - {`a??`, ErrBadClosure}, - {`\x`, ErrBadBackslash}, -} -*/ - -func compileTest(t *testing.T, expr string, error os.Error) *Regexp { - re, err := Compile(expr) - if err != error { - t.Error("compiling `", expr, "`; unexpected error: ", err.String()) - } - return re -} - -func TestGoodCompile(t *testing.T) { - for i := 0; i < len(good_re); i++ { - compileTest(t, good_re[i], nil) - } -} - -/* -func TestBadCompile(t *testing.T) { - for i := 0; i < len(bad_re); i++ { - compileTest(t, bad_re[i].re, bad_re[i].err) - } -} -*/ - -func matchTest(t *testing.T, test *FindTest) { - re := compileTest(t, test.pat, nil) - if re == nil { - return - } - m := re.MatchString(test.text) - if m != (len(test.matches) > 0) { - t.Errorf("MatchString failure on %s: %t should be %t", test, m, len(test.matches) > 0) - } - // now try bytes - m = re.Match([]byte(test.text)) - if m != (len(test.matches) > 0) { - t.Errorf("Match failure on %s: %t should be %t", test, m, len(test.matches) > 0) - } -} - -func TestMatch(t *testing.T) { - for _, test := range findTests { - matchTest(t, &test) - } -} - -func matchFunctionTest(t *testing.T, test *FindTest) { - m, err := MatchString(test.pat, test.text) - if err == nil { - return - } - if m != (len(test.matches) > 0) { - t.Errorf("Match failure on %s: %t should be %t", test, m, len(test.matches) > 0) - } -} - -func TestMatchFunction(t *testing.T) { - for _, test := range findTests { - matchFunctionTest(t, &test) - } -} - -type ReplaceTest struct { - pattern, replacement, input, output string -} - -var replaceTests = []ReplaceTest{ - // Test empty input and/or replacement, with pattern that matches the empty string. - {"", "", "", ""}, - {"", "x", "", "x"}, - {"", "", "abc", "abc"}, - {"", "x", "abc", "xaxbxcx"}, - - // Test empty input and/or replacement, with pattern that does not match the empty string. - {"b", "", "", ""}, - {"b", "x", "", ""}, - {"b", "", "abc", "ac"}, - {"b", "x", "abc", "axc"}, - {"y", "", "", ""}, - {"y", "x", "", ""}, - {"y", "", "abc", "abc"}, - {"y", "x", "abc", "abc"}, - - // Multibyte characters -- verify that we don't try to match in the middle - // of a character. - {"[a-c]*", "x", "\u65e5", "x\u65e5x"}, - {"[^\u65e5]", "x", "abc\u65e5def", "xxx\u65e5xxx"}, - - // Start and end of a string. - {"^[a-c]*", "x", "abcdabc", "xdabc"}, - {"[a-c]*$", "x", "abcdabc", "abcdx"}, - {"^[a-c]*$", "x", "abcdabc", "abcdabc"}, - {"^[a-c]*", "x", "abc", "x"}, - {"[a-c]*$", "x", "abc", "x"}, - {"^[a-c]*$", "x", "abc", "x"}, - {"^[a-c]*", "x", "dabce", "xdabce"}, - {"[a-c]*$", "x", "dabce", "dabcex"}, - {"^[a-c]*$", "x", "dabce", "dabce"}, - {"^[a-c]*", "x", "", "x"}, - {"[a-c]*$", "x", "", "x"}, - {"^[a-c]*$", "x", "", "x"}, - - {"^[a-c]+", "x", "abcdabc", "xdabc"}, - {"[a-c]+$", "x", "abcdabc", "abcdx"}, - {"^[a-c]+$", "x", "abcdabc", "abcdabc"}, - {"^[a-c]+", "x", "abc", "x"}, - {"[a-c]+$", "x", "abc", "x"}, - {"^[a-c]+$", "x", "abc", "x"}, - {"^[a-c]+", "x", "dabce", "dabce"}, - {"[a-c]+$", "x", "dabce", "dabce"}, - {"^[a-c]+$", "x", "dabce", "dabce"}, - {"^[a-c]+", "x", "", ""}, - {"[a-c]+$", "x", "", ""}, - {"^[a-c]+$", "x", "", ""}, - - // Other cases. - {"abc", "def", "abcdefg", "defdefg"}, - {"bc", "BC", "abcbcdcdedef", "aBCBCdcdedef"}, - {"abc", "", "abcdabc", "d"}, - {"x", "xXx", "xxxXxxx", "xXxxXxxXxXxXxxXxxXx"}, - {"abc", "d", "", ""}, - {"abc", "d", "abc", "d"}, - {".+", "x", "abc", "x"}, - {"[a-c]*", "x", "def", "xdxexfx"}, - {"[a-c]+", "x", "abcbcdcdedef", "xdxdedef"}, - {"[a-c]*", "x", "abcbcdcdedef", "xdxdxexdxexfx"}, -} - -type ReplaceFuncTest struct { - pattern string - replacement func(string) string - input, output string -} - -var replaceFuncTests = []ReplaceFuncTest{ - {"[a-c]", func(s string) string { return "x" + s + "y" }, "defabcdef", "defxayxbyxcydef"}, - {"[a-c]+", func(s string) string { return "x" + s + "y" }, "defabcdef", "defxabcydef"}, - {"[a-c]*", func(s string) string { return "x" + s + "y" }, "defabcdef", "xydxyexyfxabcydxyexyfxy"}, -} - -func TestReplaceAll(t *testing.T) { - for _, tc := range replaceTests { - re, err := Compile(tc.pattern) - if err != nil { - t.Errorf("Unexpected error compiling %q: %v", tc.pattern, err) - continue - } - actual := re.ReplaceAllString(tc.input, tc.replacement) - if actual != tc.output { - t.Errorf("%q.Replace(%q,%q) = %q; want %q", - tc.pattern, tc.input, tc.replacement, actual, tc.output) - } - // now try bytes - actual = string(re.ReplaceAll([]byte(tc.input), []byte(tc.replacement))) - if actual != tc.output { - t.Errorf("%q.Replace(%q,%q) = %q; want %q", - tc.pattern, tc.input, tc.replacement, actual, tc.output) - } - } -} - -func TestReplaceAllFunc(t *testing.T) { - for _, tc := range replaceFuncTests { - re, err := Compile(tc.pattern) - if err != nil { - t.Errorf("Unexpected error compiling %q: %v", tc.pattern, err) - continue - } - actual := re.ReplaceAllStringFunc(tc.input, tc.replacement) - if actual != tc.output { - t.Errorf("%q.ReplaceFunc(%q,%q) = %q; want %q", - tc.pattern, tc.input, tc.replacement, actual, tc.output) - } - // now try bytes - actual = string(re.ReplaceAllFunc([]byte(tc.input), func(s []byte) []byte { return []byte(tc.replacement(string(s))) })) - if actual != tc.output { - t.Errorf("%q.ReplaceFunc(%q,%q) = %q; want %q", - tc.pattern, tc.input, tc.replacement, actual, tc.output) - } - } -} - -type MetaTest struct { - pattern, output, literal string - isLiteral bool -} - -var metaTests = []MetaTest{ - {``, ``, ``, true}, - {`foo`, `foo`, `foo`, true}, - {`foo\.\$`, `foo\\\.\\\$`, `foo.$`, true}, // has meta but no operator - {`foo.\$`, `foo\.\\\$`, `foo`, false}, // has escaped operators and real operators - {`!@#$%^&*()_+-=[{]}\|,<.>/?~`, `!@#\$%\^&\*\(\)_\+-=\[\{\]\}\\\|,<\.>/\?~`, `!@#`, false}, -} - -func TestQuoteMeta(t *testing.T) { - for _, tc := range metaTests { - // Verify that QuoteMeta returns the expected string. - quoted := QuoteMeta(tc.pattern) - if quoted != tc.output { - t.Errorf("QuoteMeta(`%s`) = `%s`; want `%s`", - tc.pattern, quoted, tc.output) - continue - } - - // Verify that the quoted string is in fact treated as expected - // by Compile -- i.e. that it matches the original, unquoted string. - if tc.pattern != "" { - re, err := Compile(quoted) - if err != nil { - t.Errorf("Unexpected error compiling QuoteMeta(`%s`): %v", tc.pattern, err) - continue - } - src := "abc" + tc.pattern + "def" - repl := "xyz" - replaced := re.ReplaceAllString(src, repl) - expected := "abcxyzdef" - if replaced != expected { - t.Errorf("QuoteMeta(`%s`).Replace(`%s`,`%s`) = `%s`; want `%s`", - tc.pattern, src, repl, replaced, expected) - } - } - } -} - -func TestLiteralPrefix(t *testing.T) { - for _, tc := range metaTests { - // Literal method needs to scan the pattern. - re := MustCompile(tc.pattern) - str, complete := re.LiteralPrefix() - if complete != tc.isLiteral { - t.Errorf("LiteralPrefix(`%s`) = %t; want %t", tc.pattern, complete, tc.isLiteral) - } - if str != tc.literal { - t.Errorf("LiteralPrefix(`%s`) = `%s`; want `%s`", tc.pattern, str, tc.literal) - } - } -} - -type numSubexpCase struct { - input string - expected int -} - -var numSubexpCases = []numSubexpCase{ - {``, 0}, - {`.*`, 0}, - {`abba`, 0}, - {`ab(b)a`, 1}, - {`ab(.*)a`, 1}, - {`(.*)ab(.*)a`, 2}, - {`(.*)(ab)(.*)a`, 3}, - {`(.*)((a)b)(.*)a`, 4}, - {`(.*)(\(ab)(.*)a`, 3}, - {`(.*)(\(a\)b)(.*)a`, 3}, -} - -func TestNumSubexp(t *testing.T) { - for _, c := range numSubexpCases { - re := MustCompile(c.input) - n := re.NumSubexp() - if n != c.expected { - t.Errorf("NumSubexp for %q returned %d, expected %d", c.input, n, c.expected) - } - } -} - -func BenchmarkLiteral(b *testing.B) { - x := strings.Repeat("x", 50) + "y" - b.StopTimer() - re := MustCompile("y") - b.StartTimer() - for i := 0; i < b.N; i++ { - if !re.MatchString(x) { - println("no match!") - break - } - } -} - -func BenchmarkNotLiteral(b *testing.B) { - x := strings.Repeat("x", 50) + "y" - b.StopTimer() - re := MustCompile(".y") - b.StartTimer() - for i := 0; i < b.N; i++ { - if !re.MatchString(x) { - println("no match!") - break - } - } -} - -func BenchmarkMatchClass(b *testing.B) { - b.StopTimer() - x := strings.Repeat("xxxx", 20) + "w" - re := MustCompile("[abcdw]") - b.StartTimer() - for i := 0; i < b.N; i++ { - if !re.MatchString(x) { - println("no match!") - break - } - } -} - -func BenchmarkMatchClass_InRange(b *testing.B) { - b.StopTimer() - // 'b' is between 'a' and 'c', so the charclass - // range checking is no help here. - x := strings.Repeat("bbbb", 20) + "c" - re := MustCompile("[ac]") - b.StartTimer() - for i := 0; i < b.N; i++ { - if !re.MatchString(x) { - println("no match!") - break - } - } -} - -func BenchmarkReplaceAll(b *testing.B) { - x := "abcdefghijklmnopqrstuvwxyz" - b.StopTimer() - re := MustCompile("[cjrw]") - b.StartTimer() - for i := 0; i < b.N; i++ { - re.ReplaceAllString(x, "") - } -} - -func BenchmarkAnchoredLiteralShortNonMatch(b *testing.B) { - b.StopTimer() - x := []byte("abcdefghijklmnopqrstuvwxyz") - re := MustCompile("^zbc(d|e)") - b.StartTimer() - for i := 0; i < b.N; i++ { - re.Match(x) - } -} - -func BenchmarkAnchoredLiteralLongNonMatch(b *testing.B) { - b.StopTimer() - x := []byte("abcdefghijklmnopqrstuvwxyz") - for i := 0; i < 15; i++ { - x = append(x, x...) - } - re := MustCompile("^zbc(d|e)") - b.StartTimer() - for i := 0; i < b.N; i++ { - re.Match(x) - } -} - -func BenchmarkAnchoredShortMatch(b *testing.B) { - b.StopTimer() - x := []byte("abcdefghijklmnopqrstuvwxyz") - re := MustCompile("^.bc(d|e)") - b.StartTimer() - for i := 0; i < b.N; i++ { - re.Match(x) - } -} - -func BenchmarkAnchoredLongMatch(b *testing.B) { - b.StopTimer() - x := []byte("abcdefghijklmnopqrstuvwxyz") - for i := 0; i < 15; i++ { - x = append(x, x...) - } - re := MustCompile("^.bc(d|e)") - b.StartTimer() - for i := 0; i < b.N; i++ { - re.Match(x) - } -} diff --git a/libgo/go/exp/regexp/exec.go b/libgo/go/exp/regexp/exec.go deleted file mode 100644 index 0670bb9b1b4..00000000000 --- a/libgo/go/exp/regexp/exec.go +++ /dev/null @@ -1,295 +0,0 @@ -package regexp - -import "exp/regexp/syntax" - -// A queue is a 'sparse array' holding pending threads of execution. -// See http://research.swtch.com/2008/03/using-uninitialized-memory-for-fun-and.html -type queue struct { - sparse []uint32 - dense []entry -} - -// A entry is an entry on a queue. -// It holds both the instruction pc and the actual thread. -// Some queue entries are just place holders so that the machine -// knows it has considered that pc. Such entries have t == nil. -type entry struct { - pc uint32 - t *thread -} - -// A thread is the state of a single path through the machine: -// an instruction and a corresponding capture array. -// See http://swtch.com/~rsc/regexp/regexp2.html -type thread struct { - inst *syntax.Inst - cap []int -} - -// A machine holds all the state during an NFA simulation for p. -type machine struct { - re *Regexp // corresponding Regexp - p *syntax.Prog // compiled program - q0, q1 queue // two queues for runq, nextq - pool []*thread // pool of available threads - matched bool // whether a match was found - matchcap []int // capture information for the match -} - -// progMachine returns a new machine running the prog p. -func progMachine(p *syntax.Prog) *machine { - m := &machine{p: p} - n := len(m.p.Inst) - m.q0 = queue{make([]uint32, n), make([]entry, 0, n)} - m.q1 = queue{make([]uint32, n), make([]entry, 0, n)} - ncap := p.NumCap - if ncap < 2 { - ncap = 2 - } - m.matchcap = make([]int, ncap) - return m -} - -// alloc allocates a new thread with the given instruction. -// It uses the free pool if possible. -func (m *machine) alloc(i *syntax.Inst) *thread { - var t *thread - if n := len(m.pool); n > 0 { - t = m.pool[n-1] - m.pool = m.pool[:n-1] - } else { - t = new(thread) - t.cap = make([]int, cap(m.matchcap)) - } - t.cap = t.cap[:len(m.matchcap)] - t.inst = i - return t -} - -// free returns t to the free pool. -func (m *machine) free(t *thread) { - m.pool = append(m.pool, t) -} - -// match runs the machine over the input starting at pos. -// It reports whether a match was found. -// If so, m.matchcap holds the submatch information. -func (m *machine) match(i input, pos int) bool { - startCond := m.re.cond - if startCond == ^syntax.EmptyOp(0) { // impossible - return false - } - m.matched = false - for i := range m.matchcap { - m.matchcap[i] = -1 - } - runq, nextq := &m.q0, &m.q1 - rune, rune1 := endOfText, endOfText - width, width1 := 0, 0 - rune, width = i.step(pos) - if rune != endOfText { - rune1, width1 = i.step(pos + width) - } - // TODO: Let caller specify the initial flag setting. - // For now assume pos == 0 is beginning of text and - // pos != 0 is not even beginning of line. - // TODO: Word boundary. - var flag syntax.EmptyOp - if pos == 0 { - flag = syntax.EmptyBeginText | syntax.EmptyBeginLine - } - - // Update flag using lookahead rune. - if rune1 == '\n' { - flag |= syntax.EmptyEndLine - } - if rune1 == endOfText { - flag |= syntax.EmptyEndText - } - - for { - if len(runq.dense) == 0 { - if startCond&syntax.EmptyBeginText != 0 && pos != 0 { - // Anchored match, past beginning of text. - break - } - if m.matched { - // Have match; finished exploring alternatives. - break - } - if len(m.re.prefix) > 0 && rune1 != m.re.prefixRune && i.canCheckPrefix() { - // Match requires literal prefix; fast search for it. - advance := i.index(m.re, pos) - if advance < 0 { - break - } - pos += advance - rune, width = i.step(pos) - rune1, width1 = i.step(pos + width) - } - } - if !m.matched { - if len(m.matchcap) > 0 { - m.matchcap[0] = pos - } - m.add(runq, uint32(m.p.Start), pos, m.matchcap, flag) - } - // TODO: word boundary - flag = 0 - if rune == '\n' { - flag |= syntax.EmptyBeginLine - } - if rune1 == '\n' { - flag |= syntax.EmptyEndLine - } - if rune1 == endOfText { - flag |= syntax.EmptyEndText - } - m.step(runq, nextq, pos, pos+width, rune, flag) - if width == 0 { - break - } - pos += width - rune, width = rune1, width1 - if rune != endOfText { - rune1, width1 = i.step(pos + width) - } - runq, nextq = nextq, runq - } - m.clear(nextq) - return m.matched -} - -// clear frees all threads on the thread queue. -func (m *machine) clear(q *queue) { - for _, d := range q.dense { - if d.t != nil { - m.free(d.t) - } - } - q.dense = q.dense[:0] -} - -// step executes one step of the machine, running each of the threads -// on runq and appending new threads to nextq. -// The step processes the rune c (which may be endOfText), -// which starts at position pos and ends at nextPos. -// nextCond gives the setting for the empty-width flags after c. -func (m *machine) step(runq, nextq *queue, pos, nextPos, c int, nextCond syntax.EmptyOp) { - for j := 0; j < len(runq.dense); j++ { - d := &runq.dense[j] - t := d.t - if t == nil { - continue - } - /* - * If we support leftmost-longest matching: - if longest && matched && match[0] < t.cap[0] { - m.free(t) - continue - } - */ - - i := t.inst - switch i.Op { - default: - panic("bad inst") - - case syntax.InstMatch: - if len(t.cap) > 0 { - t.cap[1] = pos - copy(m.matchcap, t.cap) - } - m.matched = true - for _, d := range runq.dense[j+1:] { - if d.t != nil { - m.free(d.t) - } - } - runq.dense = runq.dense[:0] - - case syntax.InstRune: - if i.MatchRune(c) { - m.add(nextq, i.Out, nextPos, t.cap, nextCond) - } - } - m.free(t) - } - runq.dense = runq.dense[:0] -} - -// add adds an entry to q for pc, unless the q already has such an entry. -// It also recursively adds an entry for all instructions reachable from pc by following -// empty-width conditions satisfied by cond. pos gives the current position -// in the input. -func (m *machine) add(q *queue, pc uint32, pos int, cap []int, cond syntax.EmptyOp) { - if pc == 0 { - return - } - if j := q.sparse[pc]; j < uint32(len(q.dense)) && q.dense[j].pc == pc { - return - } - - j := len(q.dense) - q.dense = q.dense[:j+1] - d := &q.dense[j] - d.t = nil - d.pc = pc - q.sparse[pc] = uint32(j) - - i := &m.p.Inst[pc] - switch i.Op { - default: - panic("unhandled") - case syntax.InstFail: - // nothing - case syntax.InstAlt, syntax.InstAltMatch: - m.add(q, i.Out, pos, cap, cond) - m.add(q, i.Arg, pos, cap, cond) - case syntax.InstEmptyWidth: - if syntax.EmptyOp(i.Arg)&^cond == 0 { - m.add(q, i.Out, pos, cap, cond) - } - case syntax.InstNop: - m.add(q, i.Out, pos, cap, cond) - case syntax.InstCapture: - if int(i.Arg) < len(cap) { - opos := cap[i.Arg] - cap[i.Arg] = pos - m.add(q, i.Out, pos, cap, cond) - cap[i.Arg] = opos - } else { - m.add(q, i.Out, pos, cap, cond) - } - case syntax.InstMatch, syntax.InstRune: - t := m.alloc(i) - if len(t.cap) > 0 { - copy(t.cap, cap) - } - d.t = t - } -} - -// empty is a non-nil 0-element slice, -// so doExecute can avoid an allocation -// when 0 captures are requested from a successful match. -var empty = make([]int, 0) - -// doExecute finds the leftmost match in the input and returns -// the position of its subexpressions. -func (re *Regexp) doExecute(i input, pos int, ncap int) []int { - m := re.get() - m.matchcap = m.matchcap[:ncap] - if !m.match(i, pos) { - re.put(m) - return nil - } - if ncap == 0 { - re.put(m) - return empty // empty but not nil - } - cap := make([]int, ncap) - copy(cap, m.matchcap) - re.put(m) - return cap -} diff --git a/libgo/go/exp/regexp/find_test.go b/libgo/go/exp/regexp/find_test.go deleted file mode 100644 index dddc3484c93..00000000000 --- a/libgo/go/exp/regexp/find_test.go +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright 2010 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 regexp - -import ( - "fmt" - "strings" - "testing" -) - -// For each pattern/text pair, what is the expected output of each function? -// We can derive the textual results from the indexed results, the non-submatch -// results from the submatched results, the single results from the 'all' results, -// and the byte results from the string results. Therefore the table includes -// only the FindAllStringSubmatchIndex result. -type FindTest struct { - pat string - text string - matches [][]int -} - -func (t FindTest) String() string { - return fmt.Sprintf("pat: %#q text: %#q", t.pat, t.text) -} - -var findTests = []FindTest{ - {``, ``, build(1, 0, 0)}, - {`^abcdefg`, "abcdefg", build(1, 0, 7)}, - {`a+`, "baaab", build(1, 1, 4)}, - {"abcd..", "abcdef", build(1, 0, 6)}, - {`a`, "a", build(1, 0, 1)}, - {`x`, "y", nil}, - {`b`, "abc", build(1, 1, 2)}, - {`.`, "a", build(1, 0, 1)}, - {`.*`, "abcdef", build(1, 0, 6)}, - {`^`, "abcde", build(1, 0, 0)}, - {`$`, "abcde", build(1, 5, 5)}, - {`^abcd$`, "abcd", build(1, 0, 4)}, - {`^bcd'`, "abcdef", nil}, - {`^abcd$`, "abcde", nil}, - {`a+`, "baaab", build(1, 1, 4)}, - {`a*`, "baaab", build(3, 0, 0, 1, 4, 5, 5)}, - {`[a-z]+`, "abcd", build(1, 0, 4)}, - {`[^a-z]+`, "ab1234cd", build(1, 2, 6)}, - {`[a\-\]z]+`, "az]-bcz", build(2, 0, 4, 6, 7)}, - {`[^\n]+`, "abcd\n", build(1, 0, 4)}, - {`[日本語]+`, "日本語日本語", build(1, 0, 18)}, - {`日本語+`, "日本語", build(1, 0, 9)}, - {`日本語+`, "日本語語語語", build(1, 0, 18)}, - {`()`, "", build(1, 0, 0, 0, 0)}, - {`(a)`, "a", build(1, 0, 1, 0, 1)}, - {`(.)(.)`, "日a", build(1, 0, 4, 0, 3, 3, 4)}, - {`(.*)`, "", build(1, 0, 0, 0, 0)}, - {`(.*)`, "abcd", build(1, 0, 4, 0, 4)}, - {`(..)(..)`, "abcd", build(1, 0, 4, 0, 2, 2, 4)}, - {`(([^xyz]*)(d))`, "abcd", build(1, 0, 4, 0, 4, 0, 3, 3, 4)}, - {`((a|b|c)*(d))`, "abcd", build(1, 0, 4, 0, 4, 2, 3, 3, 4)}, - {`(((a|b|c)*)(d))`, "abcd", build(1, 0, 4, 0, 4, 0, 3, 2, 3, 3, 4)}, - {`\a\f\n\r\t\v`, "\a\f\n\r\t\v", build(1, 0, 6)}, - {`[\a\f\n\r\t\v]+`, "\a\f\n\r\t\v", build(1, 0, 6)}, - - {`a*(|(b))c*`, "aacc", build(1, 0, 4, 2, 2, -1, -1)}, - {`(.*).*`, "ab", build(1, 0, 2, 0, 2)}, - {`[.]`, ".", build(1, 0, 1)}, - {`/$`, "/abc/", build(1, 4, 5)}, - {`/$`, "/abc", nil}, - - // multiple matches - {`.`, "abc", build(3, 0, 1, 1, 2, 2, 3)}, - {`(.)`, "abc", build(3, 0, 1, 0, 1, 1, 2, 1, 2, 2, 3, 2, 3)}, - {`.(.)`, "abcd", build(2, 0, 2, 1, 2, 2, 4, 3, 4)}, - {`ab*`, "abbaab", build(3, 0, 3, 3, 4, 4, 6)}, - {`a(b*)`, "abbaab", build(3, 0, 3, 1, 3, 3, 4, 4, 4, 4, 6, 5, 6)}, - - // fixed bugs - {`ab$`, "cab", build(1, 1, 3)}, - {`axxb$`, "axxcb", nil}, - {`data`, "daXY data", build(1, 5, 9)}, - {`da(.)a$`, "daXY data", build(1, 5, 9, 7, 8)}, - {`zx+`, "zzx", build(1, 1, 3)}, - - // can backslash-escape any punctuation - {`\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\{\|\}\~`, - `!"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, build(1, 0, 31)}, - {`[\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\{\|\}\~]+`, - `!"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, build(1, 0, 31)}, - {"\\`", "`", build(1, 0, 1)}, - {"[\\`]+", "`", build(1, 0, 1)}, - - // long set of matches (longer than startSize) - { - ".", - "qwertyuiopasdfghjklzxcvbnm1234567890", - build(36, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, - 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, - 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, - 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36), - }, -} - -// build is a helper to construct a [][]int by extracting n sequences from x. -// This represents n matches with len(x)/n submatches each. -func build(n int, x ...int) [][]int { - ret := make([][]int, n) - runLength := len(x) / n - j := 0 - for i := range ret { - ret[i] = make([]int, runLength) - copy(ret[i], x[j:]) - j += runLength - if j > len(x) { - panic("invalid build entry") - } - } - return ret -} - -// First the simple cases. - -func TestFind(t *testing.T) { - for _, test := range findTests { - re := MustCompile(test.pat) - if re.String() != test.pat { - t.Errorf("String() = `%s`; should be `%s`", re.String(), test.pat) - } - result := re.Find([]byte(test.text)) - switch { - case len(test.matches) == 0 && len(result) == 0: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Errorf("expected match; got none: %s", test) - case test.matches != nil && result != nil: - expect := test.text[test.matches[0][0]:test.matches[0][1]] - if expect != string(result) { - t.Errorf("expected %q got %q: %s", expect, result, test) - } - } - } -} - -func TestFindString(t *testing.T) { - for _, test := range findTests { - result := MustCompile(test.pat).FindString(test.text) - switch { - case len(test.matches) == 0 && len(result) == 0: - // ok - case test.matches == nil && result != "": - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == "": - // Tricky because an empty result has two meanings: no match or empty match. - if test.matches[0][0] != test.matches[0][1] { - t.Errorf("expected match; got none: %s", test) - } - case test.matches != nil && result != "": - expect := test.text[test.matches[0][0]:test.matches[0][1]] - if expect != result { - t.Errorf("expected %q got %q: %s", expect, result, test) - } - } - } -} - -func testFindIndex(test *FindTest, result []int, t *testing.T) { - switch { - case len(test.matches) == 0 && len(result) == 0: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Errorf("expected match; got none: %s", test) - case test.matches != nil && result != nil: - expect := test.matches[0] - if expect[0] != result[0] || expect[1] != result[1] { - t.Errorf("expected %v got %v: %s", expect, result, test) - } - } -} - -func TestFindIndex(t *testing.T) { - for _, test := range findTests { - testFindIndex(&test, MustCompile(test.pat).FindIndex([]byte(test.text)), t) - } -} - -func TestFindStringIndex(t *testing.T) { - for _, test := range findTests { - testFindIndex(&test, MustCompile(test.pat).FindStringIndex(test.text), t) - } -} - -func TestFindReaderIndex(t *testing.T) { - for _, test := range findTests { - testFindIndex(&test, MustCompile(test.pat).FindReaderIndex(strings.NewReader(test.text)), t) - } -} - -// Now come the simple All cases. - -func TestFindAll(t *testing.T) { - for _, test := range findTests { - result := MustCompile(test.pat).FindAll([]byte(test.text), -1) - switch { - case test.matches == nil && result == nil: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Fatalf("expected match; got none: %s", test) - case test.matches != nil && result != nil: - if len(test.matches) != len(result) { - t.Errorf("expected %d matches; got %d: %s", len(test.matches), len(result), test) - continue - } - for k, e := range test.matches { - expect := test.text[e[0]:e[1]] - if expect != string(result[k]) { - t.Errorf("match %d: expected %q got %q: %s", k, expect, result[k], test) - } - } - } - } -} - -func TestFindAllString(t *testing.T) { - for _, test := range findTests { - result := MustCompile(test.pat).FindAllString(test.text, -1) - switch { - case test.matches == nil && result == nil: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Errorf("expected match; got none: %s", test) - case test.matches != nil && result != nil: - if len(test.matches) != len(result) { - t.Errorf("expected %d matches; got %d: %s", len(test.matches), len(result), test) - continue - } - for k, e := range test.matches { - expect := test.text[e[0]:e[1]] - if expect != result[k] { - t.Errorf("expected %q got %q: %s", expect, result, test) - } - } - } - } -} - -func testFindAllIndex(test *FindTest, result [][]int, t *testing.T) { - switch { - case test.matches == nil && result == nil: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Errorf("expected match; got none: %s", test) - case test.matches != nil && result != nil: - if len(test.matches) != len(result) { - t.Errorf("expected %d matches; got %d: %s", len(test.matches), len(result), test) - return - } - for k, e := range test.matches { - if e[0] != result[k][0] || e[1] != result[k][1] { - t.Errorf("match %d: expected %v got %v: %s", k, e, result[k], test) - } - } - } -} - -func TestFindAllIndex(t *testing.T) { - for _, test := range findTests { - testFindAllIndex(&test, MustCompile(test.pat).FindAllIndex([]byte(test.text), -1), t) - } -} - -func TestFindAllStringIndex(t *testing.T) { - for _, test := range findTests { - testFindAllIndex(&test, MustCompile(test.pat).FindAllStringIndex(test.text, -1), t) - } -} - -// Now come the Submatch cases. - -func testSubmatchBytes(test *FindTest, n int, submatches []int, result [][]byte, t *testing.T) { - if len(submatches) != len(result)*2 { - t.Errorf("match %d: expected %d submatches; got %d: %s", n, len(submatches)/2, len(result), test) - return - } - for k := 0; k < len(submatches); k += 2 { - if submatches[k] == -1 { - if result[k/2] != nil { - t.Errorf("match %d: expected nil got %q: %s", n, result, test) - } - continue - } - expect := test.text[submatches[k]:submatches[k+1]] - if expect != string(result[k/2]) { - t.Errorf("match %d: expected %q got %q: %s", n, expect, result, test) - return - } - } -} - -func TestFindSubmatch(t *testing.T) { - for _, test := range findTests { - result := MustCompile(test.pat).FindSubmatch([]byte(test.text)) - switch { - case test.matches == nil && result == nil: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Errorf("expected match; got none: %s", test) - case test.matches != nil && result != nil: - testSubmatchBytes(&test, 0, test.matches[0], result, t) - } - } -} - -func testSubmatchString(test *FindTest, n int, submatches []int, result []string, t *testing.T) { - if len(submatches) != len(result)*2 { - t.Errorf("match %d: expected %d submatches; got %d: %s", n, len(submatches)/2, len(result), test) - return - } - for k := 0; k < len(submatches); k += 2 { - if submatches[k] == -1 { - if result[k/2] != "" { - t.Errorf("match %d: expected nil got %q: %s", n, result, test) - } - continue - } - expect := test.text[submatches[k]:submatches[k+1]] - if expect != result[k/2] { - t.Errorf("match %d: expected %q got %q: %s", n, expect, result, test) - return - } - } -} - -func TestFindStringSubmatch(t *testing.T) { - for _, test := range findTests { - result := MustCompile(test.pat).FindStringSubmatch(test.text) - switch { - case test.matches == nil && result == nil: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Errorf("expected match; got none: %s", test) - case test.matches != nil && result != nil: - testSubmatchString(&test, 0, test.matches[0], result, t) - } - } -} - -func testSubmatchIndices(test *FindTest, n int, expect, result []int, t *testing.T) { - if len(expect) != len(result) { - t.Errorf("match %d: expected %d matches; got %d: %s", n, len(expect)/2, len(result)/2, test) - return - } - for k, e := range expect { - if e != result[k] { - t.Errorf("match %d: submatch error: expected %v got %v: %s", n, expect, result, test) - } - } -} - -func testFindSubmatchIndex(test *FindTest, result []int, t *testing.T) { - switch { - case test.matches == nil && result == nil: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Errorf("expected match; got none: %s", test) - case test.matches != nil && result != nil: - testSubmatchIndices(test, 0, test.matches[0], result, t) - } -} - -func TestFindSubmatchIndex(t *testing.T) { - for _, test := range findTests { - testFindSubmatchIndex(&test, MustCompile(test.pat).FindSubmatchIndex([]byte(test.text)), t) - } -} - -func TestFindStringSubmatchIndex(t *testing.T) { - for _, test := range findTests { - testFindSubmatchIndex(&test, MustCompile(test.pat).FindStringSubmatchIndex(test.text), t) - } -} - -func TestFindReaderSubmatchIndex(t *testing.T) { - for _, test := range findTests { - testFindSubmatchIndex(&test, MustCompile(test.pat).FindReaderSubmatchIndex(strings.NewReader(test.text)), t) - } -} - -// Now come the monster AllSubmatch cases. - -func TestFindAllSubmatch(t *testing.T) { - for _, test := range findTests { - result := MustCompile(test.pat).FindAllSubmatch([]byte(test.text), -1) - switch { - case test.matches == nil && result == nil: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Errorf("expected match; got none: %s", test) - case len(test.matches) != len(result): - t.Errorf("expected %d matches; got %d: %s", len(test.matches), len(result), test) - case test.matches != nil && result != nil: - for k, match := range test.matches { - testSubmatchBytes(&test, k, match, result[k], t) - } - } - } -} - -func TestFindAllStringSubmatch(t *testing.T) { - for _, test := range findTests { - result := MustCompile(test.pat).FindAllStringSubmatch(test.text, -1) - switch { - case test.matches == nil && result == nil: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Errorf("expected match; got none: %s", test) - case len(test.matches) != len(result): - t.Errorf("expected %d matches; got %d: %s", len(test.matches), len(result), test) - case test.matches != nil && result != nil: - for k, match := range test.matches { - testSubmatchString(&test, k, match, result[k], t) - } - } - } -} - -func testFindAllSubmatchIndex(test *FindTest, result [][]int, t *testing.T) { - switch { - case test.matches == nil && result == nil: - // ok - case test.matches == nil && result != nil: - t.Errorf("expected no match; got one: %s", test) - case test.matches != nil && result == nil: - t.Errorf("expected match; got none: %s", test) - case len(test.matches) != len(result): - t.Errorf("expected %d matches; got %d: %s", len(test.matches), len(result), test) - case test.matches != nil && result != nil: - for k, match := range test.matches { - testSubmatchIndices(test, k, match, result[k], t) - } - } -} - -func TestFindAllSubmatchIndex(t *testing.T) { - for _, test := range findTests { - testFindAllSubmatchIndex(&test, MustCompile(test.pat).FindAllSubmatchIndex([]byte(test.text), -1), t) - } -} - -func TestFindAllStringSubmatchIndex(t *testing.T) { - for _, test := range findTests { - testFindAllSubmatchIndex(&test, MustCompile(test.pat).FindAllStringSubmatchIndex(test.text, -1), t) - } -} diff --git a/libgo/go/exp/regexp/regexp.go b/libgo/go/exp/regexp/regexp.go deleted file mode 100644 index 1b75900f816..00000000000 --- a/libgo/go/exp/regexp/regexp.go +++ /dev/null @@ -1,795 +0,0 @@ -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package regexp implements a simple regular expression library. -// -// The syntax of the regular expressions accepted is the same -// general syntax used by Perl, Python, and other languages. -// More precisely, it is the syntax accepted by RE2 and described at -// http://code.google.com/p/re2/wiki/Syntax, except for \C. -// -// All characters are UTF-8-encoded code points. -// -// There are 16 methods of Regexp that match a regular expression and identify -// the matched text. Their names are matched by this regular expression: -// -// Find(All)?(String)?(Submatch)?(Index)? -// -// If 'All' is present, the routine matches successive non-overlapping -// matches of the entire expression. Empty matches abutting a preceding -// match are ignored. The return value is a slice containing the successive -// return values of the corresponding non-'All' routine. These routines take -// an extra integer argument, n; if n >= 0, the function returns at most n -// matches/submatches. -// -// If 'String' is present, the argument is a string; otherwise it is a slice -// of bytes; return values are adjusted as appropriate. -// -// If 'Submatch' is present, the return value is a slice identifying the -// successive submatches of the expression. Submatches are matches of -// parenthesized subexpressions within the regular expression, numbered from -// left to right in order of opening parenthesis. Submatch 0 is the match of -// the entire expression, submatch 1 the match of the first parenthesized -// subexpression, and so on. -// -// If 'Index' is present, matches and submatches are identified by byte index -// pairs within the input string: result[2*n:2*n+1] identifies the indexes of -// the nth submatch. The pair for n==0 identifies the match of the entire -// expression. If 'Index' is not present, the match is identified by the -// text of the match/submatch. If an index is negative, it means that -// subexpression did not match any string in the input. -// -// There is also a subset of the methods that can be applied to text read -// from a RuneReader: -// -// MatchReader, FindReaderIndex, FindReaderSubmatchIndex -// -// This set may grow. Note that regular expression matches may need to -// examine text beyond the text returned by a match, so the methods that -// match text from a RuneReader may read arbitrarily far into the input -// before returning. -// -// (There are a few other methods that do not match this pattern.) -// -package regexp - -import ( - "bytes" - "exp/regexp/syntax" - "io" - "os" - "strings" - "sync" - "utf8" -) - -var debug = false - -// Error is the local type for a parsing error. -type Error string - -func (e Error) String() string { - return string(e) -} - -// Regexp is the representation of a compiled regular expression. -// The public interface is entirely through methods. -// A Regexp is safe for concurrent use by multiple goroutines. -type Regexp struct { - // read-only after Compile - expr string // as passed to Compile - prog *syntax.Prog // compiled program - prefix string // required prefix in unanchored matches - prefixBytes []byte // prefix, as a []byte - prefixComplete bool // prefix is the entire regexp - prefixRune int // first rune in prefix - cond syntax.EmptyOp // empty-width conditions required at start of match - - // cache of machines for running regexp - mu sync.Mutex - machine []*machine -} - -// String returns the source text used to compile the regular expression. -func (re *Regexp) String() string { - return re.expr -} - -// Compile parses a regular expression and returns, if successful, a Regexp -// object that can be used to match against text. -func Compile(expr string) (*Regexp, os.Error) { - re, err := syntax.Parse(expr, syntax.Perl) - if err != nil { - return nil, err - } - prog, err := syntax.Compile(re) - if err != nil { - return nil, err - } - regexp := &Regexp{ - expr: expr, - prog: prog, - } - regexp.prefix, regexp.prefixComplete = prog.Prefix() - if regexp.prefix != "" { - // TODO(rsc): Remove this allocation by adding - // IndexString to package bytes. - regexp.prefixBytes = []byte(regexp.prefix) - regexp.prefixRune, _ = utf8.DecodeRuneInString(regexp.prefix) - } - regexp.cond = prog.StartCond() - return regexp, nil -} - -// get returns a machine to use for matching re. -// It uses the re's machine cache if possible, to avoid -// unnecessary allocation. -func (re *Regexp) get() *machine { - re.mu.Lock() - if n := len(re.machine); n > 0 { - z := re.machine[n-1] - re.machine = re.machine[:n-1] - re.mu.Unlock() - return z - } - re.mu.Unlock() - z := progMachine(re.prog) - z.re = re - return z -} - -// put returns a machine to the re's machine cache. -// There is no attempt to limit the size of the cache, so it will -// grow to the maximum number of simultaneous matches -// run using re. (The cache empties when re gets garbage collected.) -func (re *Regexp) put(z *machine) { - re.mu.Lock() - re.machine = append(re.machine, z) - re.mu.Unlock() -} - -// MustCompile is like Compile but panics if the expression cannot be parsed. -// It simplifies safe initialization of global variables holding compiled regular -// expressions. -func MustCompile(str string) *Regexp { - regexp, error := Compile(str) - if error != nil { - panic(`regexp: compiling "` + str + `": ` + error.String()) - } - return regexp -} - -// NumSubexp returns the number of parenthesized subexpressions in this Regexp. -func (re *Regexp) NumSubexp() int { - // NumCap/2 because captures count ( and ) separately. - // -1 because NumCap counts $0 but NumSubexp does not. - return re.prog.NumCap/2 - 1 -} - -const endOfText = -1 - -// input abstracts different representations of the input text. It provides -// one-character lookahead. -type input interface { - step(pos int) (rune int, width int) // advance one rune - canCheckPrefix() bool // can we look ahead without losing info? - hasPrefix(re *Regexp) bool - index(re *Regexp, pos int) int -} - -// inputString scans a string. -type inputString struct { - str string -} - -func newInputString(str string) *inputString { - return &inputString{str: str} -} - -func (i *inputString) step(pos int) (int, int) { - if pos < len(i.str) { - return utf8.DecodeRuneInString(i.str[pos:len(i.str)]) - } - return endOfText, 0 -} - -func (i *inputString) canCheckPrefix() bool { - return true -} - -func (i *inputString) hasPrefix(re *Regexp) bool { - return strings.HasPrefix(i.str, re.prefix) -} - -func (i *inputString) index(re *Regexp, pos int) int { - return strings.Index(i.str[pos:], re.prefix) -} - -// inputBytes scans a byte slice. -type inputBytes struct { - str []byte -} - -func newInputBytes(str []byte) *inputBytes { - return &inputBytes{str: str} -} - -func (i *inputBytes) step(pos int) (int, int) { - if pos < len(i.str) { - return utf8.DecodeRune(i.str[pos:len(i.str)]) - } - return endOfText, 0 -} - -func (i *inputBytes) canCheckPrefix() bool { - return true -} - -func (i *inputBytes) hasPrefix(re *Regexp) bool { - return bytes.HasPrefix(i.str, re.prefixBytes) -} - -func (i *inputBytes) index(re *Regexp, pos int) int { - return bytes.Index(i.str[pos:], re.prefixBytes) -} - -// inputReader scans a RuneReader. -type inputReader struct { - r io.RuneReader - atEOT bool - pos int -} - -func newInputReader(r io.RuneReader) *inputReader { - return &inputReader{r: r} -} - -func (i *inputReader) step(pos int) (int, int) { - if !i.atEOT && pos != i.pos { - return endOfText, 0 - - } - r, w, err := i.r.ReadRune() - if err != nil { - i.atEOT = true - return endOfText, 0 - } - i.pos += w - return r, w -} - -func (i *inputReader) canCheckPrefix() bool { - return false -} - -func (i *inputReader) hasPrefix(re *Regexp) bool { - return false -} - -func (i *inputReader) index(re *Regexp, pos int) int { - return -1 -} - -// LiteralPrefix returns a literal string that must begin any match -// of the regular expression re. It returns the boolean true if the -// literal string comprises the entire regular expression. -func (re *Regexp) LiteralPrefix() (prefix string, complete bool) { - return re.prefix, re.prefixComplete -} - -// MatchReader returns whether the Regexp matches the text read by the -// RuneReader. The return value is a boolean: true for match, false for no -// match. -func (re *Regexp) MatchReader(r io.RuneReader) bool { - return re.doExecute(newInputReader(r), 0, 0) != nil -} - -// MatchString returns whether the Regexp matches the string s. -// The return value is a boolean: true for match, false for no match. -func (re *Regexp) MatchString(s string) bool { - return re.doExecute(newInputString(s), 0, 0) != nil -} - -// Match returns whether the Regexp matches the byte slice b. -// The return value is a boolean: true for match, false for no match. -func (re *Regexp) Match(b []byte) bool { - return re.doExecute(newInputBytes(b), 0, 0) != nil -} - -// MatchReader checks whether a textual regular expression matches the text -// read by the RuneReader. More complicated queries need to use Compile and -// the full Regexp interface. -func MatchReader(pattern string, r io.RuneReader) (matched bool, error os.Error) { - re, err := Compile(pattern) - if err != nil { - return false, err - } - return re.MatchReader(r), nil -} - -// MatchString checks whether a textual regular expression -// matches a string. More complicated queries need -// to use Compile and the full Regexp interface. -func MatchString(pattern string, s string) (matched bool, error os.Error) { - re, err := Compile(pattern) - if err != nil { - return false, err - } - return re.MatchString(s), nil -} - -// Match checks whether a textual regular expression -// matches a byte slice. More complicated queries need -// to use Compile and the full Regexp interface. -func Match(pattern string, b []byte) (matched bool, error os.Error) { - re, err := Compile(pattern) - if err != nil { - return false, err - } - return re.Match(b), nil -} - -// ReplaceAllString returns a copy of src in which all matches for the Regexp -// have been replaced by repl. No support is provided for expressions -// (e.g. \1 or $1) in the replacement string. -func (re *Regexp) ReplaceAllString(src, repl string) string { - return re.ReplaceAllStringFunc(src, func(string) string { return repl }) -} - -// ReplaceAllStringFunc returns a copy of src in which all matches for the -// Regexp have been replaced by the return value of of function repl (whose -// first argument is the matched string). No support is provided for -// expressions (e.g. \1 or $1) in the replacement string. -func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string { - lastMatchEnd := 0 // end position of the most recent match - searchPos := 0 // position where we next look for a match - buf := new(bytes.Buffer) - for searchPos <= len(src) { - a := re.doExecute(newInputString(src), searchPos, 2) - if len(a) == 0 { - break // no more matches - } - - // Copy the unmatched characters before this match. - io.WriteString(buf, src[lastMatchEnd:a[0]]) - - // Now insert a copy of the replacement string, but not for a - // match of the empty string immediately after another match. - // (Otherwise, we get double replacement for patterns that - // match both empty and nonempty strings.) - if a[1] > lastMatchEnd || a[0] == 0 { - io.WriteString(buf, repl(src[a[0]:a[1]])) - } - lastMatchEnd = a[1] - - // Advance past this match; always advance at least one character. - _, width := utf8.DecodeRuneInString(src[searchPos:]) - if searchPos+width > a[1] { - searchPos += width - } else if searchPos+1 > a[1] { - // This clause is only needed at the end of the input - // string. In that case, DecodeRuneInString returns width=0. - searchPos++ - } else { - searchPos = a[1] - } - } - - // Copy the unmatched characters after the last match. - io.WriteString(buf, src[lastMatchEnd:]) - - return buf.String() -} - -// ReplaceAll returns a copy of src in which all matches for the Regexp -// have been replaced by repl. No support is provided for expressions -// (e.g. \1 or $1) in the replacement text. -func (re *Regexp) ReplaceAll(src, repl []byte) []byte { - return re.ReplaceAllFunc(src, func([]byte) []byte { return repl }) -} - -// ReplaceAllFunc returns a copy of src in which all matches for the -// Regexp have been replaced by the return value of of function repl (whose -// first argument is the matched []byte). No support is provided for -// expressions (e.g. \1 or $1) in the replacement string. -func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte { - lastMatchEnd := 0 // end position of the most recent match - searchPos := 0 // position where we next look for a match - buf := new(bytes.Buffer) - for searchPos <= len(src) { - a := re.doExecute(newInputBytes(src), searchPos, 2) - if len(a) == 0 { - break // no more matches - } - - // Copy the unmatched characters before this match. - buf.Write(src[lastMatchEnd:a[0]]) - - // Now insert a copy of the replacement string, but not for a - // match of the empty string immediately after another match. - // (Otherwise, we get double replacement for patterns that - // match both empty and nonempty strings.) - if a[1] > lastMatchEnd || a[0] == 0 { - buf.Write(repl(src[a[0]:a[1]])) - } - lastMatchEnd = a[1] - - // Advance past this match; always advance at least one character. - _, width := utf8.DecodeRune(src[searchPos:]) - if searchPos+width > a[1] { - searchPos += width - } else if searchPos+1 > a[1] { - // This clause is only needed at the end of the input - // string. In that case, DecodeRuneInString returns width=0. - searchPos++ - } else { - searchPos = a[1] - } - } - - // Copy the unmatched characters after the last match. - buf.Write(src[lastMatchEnd:]) - - return buf.Bytes() -} - -var specialBytes = []byte(`\.+*?()|[]{}^$`) - -func special(b byte) bool { - return bytes.IndexByte(specialBytes, b) >= 0 -} - -// QuoteMeta returns a string that quotes all regular expression metacharacters -// inside the argument text; the returned string is a regular expression matching -// the literal text. For example, QuoteMeta(`[foo]`) returns `\[foo\]`. -func QuoteMeta(s string) string { - b := make([]byte, 2*len(s)) - - // A byte loop is correct because all metacharacters are ASCII. - j := 0 - for i := 0; i < len(s); i++ { - if special(s[i]) { - b[j] = '\\' - j++ - } - b[j] = s[i] - j++ - } - return string(b[0:j]) -} - -// Find matches in slice b if b is non-nil, otherwise find matches in string s. -func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) { - var end int - if b == nil { - end = len(s) - } else { - end = len(b) - } - - for pos, i, prevMatchEnd := 0, 0, -1; i < n && pos <= end; { - var in input - if b == nil { - in = newInputString(s) - } else { - in = newInputBytes(b) - } - matches := re.doExecute(in, pos, re.prog.NumCap) - if len(matches) == 0 { - break - } - - accept := true - if matches[1] == pos { - // We've found an empty match. - if matches[0] == prevMatchEnd { - // We don't allow an empty match right - // after a previous match, so ignore it. - accept = false - } - var width int - // TODO: use step() - if b == nil { - _, width = utf8.DecodeRuneInString(s[pos:end]) - } else { - _, width = utf8.DecodeRune(b[pos:end]) - } - if width > 0 { - pos += width - } else { - pos = end + 1 - } - } else { - pos = matches[1] - } - prevMatchEnd = matches[1] - - if accept { - deliver(matches) - i++ - } - } -} - -// Find returns a slice holding the text of the leftmost match in b of the regular expression. -// A return value of nil indicates no match. -func (re *Regexp) Find(b []byte) []byte { - a := re.doExecute(newInputBytes(b), 0, 2) - if a == nil { - return nil - } - return b[a[0]:a[1]] -} - -// FindIndex returns a two-element slice of integers defining the location of -// the leftmost match in b of the regular expression. The match itself is at -// b[loc[0]:loc[1]]. -// A return value of nil indicates no match. -func (re *Regexp) FindIndex(b []byte) (loc []int) { - a := re.doExecute(newInputBytes(b), 0, 2) - if a == nil { - return nil - } - return a[0:2] -} - -// FindString returns a string holding the text of the leftmost match in s of the regular -// expression. If there is no match, the return value is an empty string, -// but it will also be empty if the regular expression successfully matches -// an empty string. Use FindStringIndex or FindStringSubmatch if it is -// necessary to distinguish these cases. -func (re *Regexp) FindString(s string) string { - a := re.doExecute(newInputString(s), 0, 2) - if a == nil { - return "" - } - return s[a[0]:a[1]] -} - -// FindStringIndex returns a two-element slice of integers defining the -// location of the leftmost match in s of the regular expression. The match -// itself is at s[loc[0]:loc[1]]. -// A return value of nil indicates no match. -func (re *Regexp) FindStringIndex(s string) []int { - a := re.doExecute(newInputString(s), 0, 2) - if a == nil { - return nil - } - return a[0:2] -} - -// FindReaderIndex returns a two-element slice of integers defining the -// location of the leftmost match of the regular expression in text read from -// the RuneReader. The match itself is at s[loc[0]:loc[1]]. A return -// value of nil indicates no match. -func (re *Regexp) FindReaderIndex(r io.RuneReader) []int { - a := re.doExecute(newInputReader(r), 0, 2) - if a == nil { - return nil - } - return a[0:2] -} - -// FindSubmatch returns a slice of slices holding the text of the leftmost -// match of the regular expression in b and the matches, if any, of its -// subexpressions, as defined by the 'Submatch' descriptions in the package -// comment. -// A return value of nil indicates no match. -func (re *Regexp) FindSubmatch(b []byte) [][]byte { - a := re.doExecute(newInputBytes(b), 0, re.prog.NumCap) - if a == nil { - return nil - } - ret := make([][]byte, len(a)/2) - for i := range ret { - if a[2*i] >= 0 { - ret[i] = b[a[2*i]:a[2*i+1]] - } - } - return ret -} - -// FindSubmatchIndex returns a slice holding the index pairs identifying the -// leftmost match of the regular expression in b and the matches, if any, of -// its subexpressions, as defined by the 'Submatch' and 'Index' descriptions -// in the package comment. -// A return value of nil indicates no match. -func (re *Regexp) FindSubmatchIndex(b []byte) []int { - return re.doExecute(newInputBytes(b), 0, re.prog.NumCap) -} - -// FindStringSubmatch returns a slice of strings holding the text of the -// leftmost match of the regular expression in s and the matches, if any, of -// its subexpressions, as defined by the 'Submatch' description in the -// package comment. -// A return value of nil indicates no match. -func (re *Regexp) FindStringSubmatch(s string) []string { - a := re.doExecute(newInputString(s), 0, re.prog.NumCap) - if a == nil { - return nil - } - ret := make([]string, len(a)/2) - for i := range ret { - if a[2*i] >= 0 { - ret[i] = s[a[2*i]:a[2*i+1]] - } - } - return ret -} - -// FindStringSubmatchIndex returns a slice holding the index pairs -// identifying the leftmost match of the regular expression in s and the -// matches, if any, of its subexpressions, as defined by the 'Submatch' and -// 'Index' descriptions in the package comment. -// A return value of nil indicates no match. -func (re *Regexp) FindStringSubmatchIndex(s string) []int { - return re.doExecute(newInputString(s), 0, re.prog.NumCap) -} - -// FindReaderSubmatchIndex returns a slice holding the index pairs -// identifying the leftmost match of the regular expression of text read by -// the RuneReader, and the matches, if any, of its subexpressions, as defined -// by the 'Submatch' and 'Index' descriptions in the package comment. A -// return value of nil indicates no match. -func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int { - return re.doExecute(newInputReader(r), 0, re.prog.NumCap) -} - -const startSize = 10 // The size at which to start a slice in the 'All' routines. - -// FindAll is the 'All' version of Find; it returns a slice of all successive -// matches of the expression, as defined by the 'All' description in the -// package comment. -// A return value of nil indicates no match. -func (re *Regexp) FindAll(b []byte, n int) [][]byte { - if n < 0 { - n = len(b) + 1 - } - result := make([][]byte, 0, startSize) - re.allMatches("", b, n, func(match []int) { - result = append(result, b[match[0]:match[1]]) - }) - if len(result) == 0 { - return nil - } - return result -} - -// FindAllIndex is the 'All' version of FindIndex; it returns a slice of all -// successive matches of the expression, as defined by the 'All' description -// in the package comment. -// A return value of nil indicates no match. -func (re *Regexp) FindAllIndex(b []byte, n int) [][]int { - if n < 0 { - n = len(b) + 1 - } - result := make([][]int, 0, startSize) - re.allMatches("", b, n, func(match []int) { - result = append(result, match[0:2]) - }) - if len(result) == 0 { - return nil - } - return result -} - -// FindAllString is the 'All' version of FindString; it returns a slice of all -// successive matches of the expression, as defined by the 'All' description -// in the package comment. -// A return value of nil indicates no match. -func (re *Regexp) FindAllString(s string, n int) []string { - if n < 0 { - n = len(s) + 1 - } - result := make([]string, 0, startSize) - re.allMatches(s, nil, n, func(match []int) { - result = append(result, s[match[0]:match[1]]) - }) - if len(result) == 0 { - return nil - } - return result -} - -// FindAllStringIndex is the 'All' version of FindStringIndex; it returns a -// slice of all successive matches of the expression, as defined by the 'All' -// description in the package comment. -// A return value of nil indicates no match. -func (re *Regexp) FindAllStringIndex(s string, n int) [][]int { - if n < 0 { - n = len(s) + 1 - } - result := make([][]int, 0, startSize) - re.allMatches(s, nil, n, func(match []int) { - result = append(result, match[0:2]) - }) - if len(result) == 0 { - return nil - } - return result -} - -// FindAllSubmatch is the 'All' version of FindSubmatch; it returns a slice -// of all successive matches of the expression, as defined by the 'All' -// description in the package comment. -// A return value of nil indicates no match. -func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte { - if n < 0 { - n = len(b) + 1 - } - result := make([][][]byte, 0, startSize) - re.allMatches("", b, n, func(match []int) { - slice := make([][]byte, len(match)/2) - for j := range slice { - if match[2*j] >= 0 { - slice[j] = b[match[2*j]:match[2*j+1]] - } - } - result = append(result, slice) - }) - if len(result) == 0 { - return nil - } - return result -} - -// FindAllSubmatchIndex is the 'All' version of FindSubmatchIndex; it returns -// a slice of all successive matches of the expression, as defined by the -// 'All' description in the package comment. -// A return value of nil indicates no match. -func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int { - if n < 0 { - n = len(b) + 1 - } - result := make([][]int, 0, startSize) - re.allMatches("", b, n, func(match []int) { - result = append(result, match) - }) - if len(result) == 0 { - return nil - } - return result -} - -// FindAllStringSubmatch is the 'All' version of FindStringSubmatch; it -// returns a slice of all successive matches of the expression, as defined by -// the 'All' description in the package comment. -// A return value of nil indicates no match. -func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string { - if n < 0 { - n = len(s) + 1 - } - result := make([][]string, 0, startSize) - re.allMatches(s, nil, n, func(match []int) { - slice := make([]string, len(match)/2) - for j := range slice { - if match[2*j] >= 0 { - slice[j] = s[match[2*j]:match[2*j+1]] - } - } - result = append(result, slice) - }) - if len(result) == 0 { - return nil - } - return result -} - -// FindAllStringSubmatchIndex is the 'All' version of -// FindStringSubmatchIndex; it returns a slice of all successive matches of -// the expression, as defined by the 'All' description in the package -// comment. -// A return value of nil indicates no match. -func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int { - if n < 0 { - n = len(s) + 1 - } - result := make([][]int, 0, startSize) - re.allMatches(s, nil, n, func(match []int) { - result = append(result, match) - }) - if len(result) == 0 { - return nil - } - return result -} diff --git a/libgo/go/exp/regexp/syntax/compile.go b/libgo/go/exp/regexp/syntax/compile.go deleted file mode 100644 index 5ea2425c3aa..00000000000 --- a/libgo/go/exp/regexp/syntax/compile.go +++ /dev/null @@ -1,269 +0,0 @@ -package syntax - -import ( - "os" - "unicode" -) - -// A patchList is a list of instruction pointers that need to be filled in (patched). -// Because the pointers haven't been filled in yet, we can reuse their storage -// to hold the list. It's kind of sleazy, but works well in practice. -// See http://swtch.com/~rsc/regexp/regexp1.html for inspiration. -// -// These aren't really pointers: they're integers, so we can reinterpret them -// this way without using package unsafe. A value l denotes -// p.inst[l>>1].Out (l&1==0) or .Arg (l&1==1). -// l == 0 denotes the empty list, okay because we start every program -// with a fail instruction, so we'll never want to point at its output link. -type patchList uint32 - -func (l patchList) next(p *Prog) patchList { - i := &p.Inst[l>>1] - if l&1 == 0 { - return patchList(i.Out) - } - return patchList(i.Arg) -} - -func (l patchList) patch(p *Prog, val uint32) { - for l != 0 { - i := &p.Inst[l>>1] - if l&1 == 0 { - l = patchList(i.Out) - i.Out = val - } else { - l = patchList(i.Arg) - i.Arg = val - } - } -} - -func (l1 patchList) append(p *Prog, l2 patchList) patchList { - if l1 == 0 { - return l2 - } - if l2 == 0 { - return l1 - } - - last := l1 - for { - next := last.next(p) - if next == 0 { - break - } - last = next - } - - i := &p.Inst[last>>1] - if last&1 == 0 { - i.Out = uint32(l2) - } else { - i.Arg = uint32(l2) - } - return l1 -} - -// A frag represents a compiled program fragment. -type frag struct { - i uint32 // index of first instruction - out patchList // where to record end instruction -} - -type compiler struct { - p *Prog -} - -// Compile compiles the regexp into a program to be executed. -func Compile(re *Regexp) (*Prog, os.Error) { - var c compiler - c.init() - f := c.compile(re) - f.out.patch(c.p, c.inst(InstMatch).i) - c.p.Start = int(f.i) - return c.p, nil -} - -func (c *compiler) init() { - c.p = new(Prog) - c.p.NumCap = 2 // implicit ( and ) for whole match $0 - c.inst(InstFail) -} - -var anyRuneNotNL = []int{0, '\n' - 1, '\n' - 1, unicode.MaxRune} -var anyRune = []int{0, unicode.MaxRune} - -func (c *compiler) compile(re *Regexp) frag { - switch re.Op { - case OpNoMatch: - return c.fail() - case OpEmptyMatch: - return c.nop() - case OpLiteral: - if len(re.Rune) == 0 { - return c.nop() - } - var f frag - for j := range re.Rune { - f1 := c.rune(re.Rune[j : j+1]) - if j == 0 { - f = f1 - } else { - f = c.cat(f, f1) - } - } - return f - case OpCharClass: - return c.rune(re.Rune) - case OpAnyCharNotNL: - return c.rune(anyRuneNotNL) - case OpAnyChar: - return c.rune(anyRune) - case OpBeginLine: - return c.empty(EmptyBeginLine) - case OpEndLine: - return c.empty(EmptyEndLine) - case OpBeginText: - return c.empty(EmptyBeginText) - case OpEndText: - return c.empty(EmptyEndText) - case OpWordBoundary: - return c.empty(EmptyWordBoundary) - case OpNoWordBoundary: - return c.empty(EmptyNoWordBoundary) - case OpCapture: - bra := c.cap(uint32(re.Cap << 1)) - sub := c.compile(re.Sub[0]) - ket := c.cap(uint32(re.Cap<<1 | 1)) - return c.cat(c.cat(bra, sub), ket) - case OpStar: - return c.star(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0) - case OpPlus: - return c.plus(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0) - case OpQuest: - return c.quest(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0) - case OpConcat: - if len(re.Sub) == 0 { - return c.nop() - } - var f frag - for i, sub := range re.Sub { - if i == 0 { - f = c.compile(sub) - } else { - f = c.cat(f, c.compile(sub)) - } - } - return f - case OpAlternate: - var f frag - for _, sub := range re.Sub { - f = c.alt(f, c.compile(sub)) - } - return f - } - panic("regexp: unhandled case in compile") -} - -func (c *compiler) inst(op InstOp) frag { - // TODO: impose length limit - f := frag{i: uint32(len(c.p.Inst))} - c.p.Inst = append(c.p.Inst, Inst{Op: op}) - return f -} - -func (c *compiler) nop() frag { - f := c.inst(InstNop) - f.out = patchList(f.i << 1) - return f -} - -func (c *compiler) fail() frag { - return frag{} -} - -func (c *compiler) cap(arg uint32) frag { - f := c.inst(InstCapture) - f.out = patchList(f.i << 1) - c.p.Inst[f.i].Arg = arg - - if c.p.NumCap < int(arg)+1 { - c.p.NumCap = int(arg) + 1 - } - return f -} - -func (c *compiler) cat(f1, f2 frag) frag { - // concat of failure is failure - if f1.i == 0 || f2.i == 0 { - return frag{} - } - - // TODO: elide nop - - f1.out.patch(c.p, f2.i) - return frag{f1.i, f2.out} -} - -func (c *compiler) alt(f1, f2 frag) frag { - // alt of failure is other - if f1.i == 0 { - return f2 - } - if f2.i == 0 { - return f1 - } - - f := c.inst(InstAlt) - i := &c.p.Inst[f.i] - i.Out = f1.i - i.Arg = f2.i - f.out = f1.out.append(c.p, f2.out) - return f -} - -func (c *compiler) quest(f1 frag, nongreedy bool) frag { - f := c.inst(InstAlt) - i := &c.p.Inst[f.i] - if nongreedy { - i.Arg = f1.i - f.out = patchList(f.i << 1) - } else { - i.Out = f1.i - f.out = patchList(f.i<<1 | 1) - } - f.out = f.out.append(c.p, f1.out) - return f -} - -func (c *compiler) star(f1 frag, nongreedy bool) frag { - f := c.inst(InstAlt) - i := &c.p.Inst[f.i] - if nongreedy { - i.Arg = f1.i - f.out = patchList(f.i << 1) - } else { - i.Out = f1.i - f.out = patchList(f.i<<1 | 1) - } - f1.out.patch(c.p, f.i) - return f -} - -func (c *compiler) plus(f1 frag, nongreedy bool) frag { - return frag{f1.i, c.star(f1, nongreedy).out} -} - -func (c *compiler) empty(op EmptyOp) frag { - f := c.inst(InstEmptyWidth) - c.p.Inst[f.i].Arg = uint32(op) - f.out = patchList(f.i << 1) - return f -} - -func (c *compiler) rune(rune []int) frag { - f := c.inst(InstRune) - c.p.Inst[f.i].Rune = rune - f.out = patchList(f.i << 1) - return f -} diff --git a/libgo/go/exp/regexp/syntax/parse.go b/libgo/go/exp/regexp/syntax/parse.go deleted file mode 100644 index 4eed182687b..00000000000 --- a/libgo/go/exp/regexp/syntax/parse.go +++ /dev/null @@ -1,1797 +0,0 @@ -// 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 syntax - -import ( - "os" - "sort" - "strings" - "unicode" - "utf8" -) - -// An Error describes a failure to parse a regular expression -// and gives the offending expression. -type Error struct { - Code ErrorCode - Expr string -} - -func (e *Error) String() string { - return "error parsing regexp: " + e.Code.String() + ": `" + e.Expr + "`" -} - -// An ErrorCode describes a failure to parse a regular expression. -type ErrorCode string - -const ( - // Unexpected error - ErrInternalError ErrorCode = "regexp/syntax: internal error" - - // Parse errors - ErrInvalidCharClass ErrorCode = "invalid character class" - ErrInvalidCharRange ErrorCode = "invalid character class range" - ErrInvalidEscape ErrorCode = "invalid escape sequence" - ErrInvalidNamedCapture ErrorCode = "invalid named capture" - ErrInvalidPerlOp ErrorCode = "invalid or unsupported Perl syntax" - ErrInvalidRepeatOp ErrorCode = "invalid nested repetition operator" - ErrInvalidRepeatSize ErrorCode = "invalid repeat count" - ErrInvalidUTF8 ErrorCode = "invalid UTF-8" - ErrMissingBracket ErrorCode = "missing closing ]" - ErrMissingParen ErrorCode = "missing closing )" - ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator" - ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression" -) - -func (e ErrorCode) String() string { - return string(e) -} - -// Flags control the behavior of the parser and record information about regexp context. -type Flags uint16 - -const ( - FoldCase Flags = 1 << iota // case-insensitive match - Literal // treat pattern as literal string - ClassNL // allow character classes like [^a-z] and [[:space:]] to match newline - DotNL // allow . to match newline - OneLine // treat ^ and $ as only matching at beginning and end of text - NonGreedy // make repetition operators default to non-greedy - PerlX // allow Perl extensions - UnicodeGroups // allow \p{Han}, \P{Han} for Unicode group and negation - WasDollar // regexp OpEndText was $, not \z - Simple // regexp contains no counted repetition - - MatchNL = ClassNL | DotNL - - Perl = ClassNL | OneLine | PerlX | UnicodeGroups // as close to Perl as possible - POSIX Flags = 0 // POSIX syntax -) - -// Pseudo-ops for parsing stack. -const ( - opLeftParen = opPseudo + iota - opVerticalBar -) - -type parser struct { - flags Flags // parse mode flags - stack []*Regexp // stack of parsed expressions - free *Regexp - numCap int // number of capturing groups seen - wholeRegexp string - tmpClass []int // temporary char class work space -} - -func (p *parser) newRegexp(op Op) *Regexp { - re := p.free - if re != nil { - p.free = re.Sub0[0] - *re = Regexp{} - } else { - re = new(Regexp) - } - re.Op = op - return re -} - -func (p *parser) reuse(re *Regexp) { - re.Sub0[0] = p.free - p.free = re -} - -// Parse stack manipulation. - -// push pushes the regexp re onto the parse stack and returns the regexp. -func (p *parser) push(re *Regexp) *Regexp { - if re.Op == OpCharClass && len(re.Rune) == 2 && re.Rune[0] == re.Rune[1] { - // Single rune. - if p.maybeConcat(re.Rune[0], p.flags&^FoldCase) { - return nil - } - re.Op = OpLiteral - re.Rune = re.Rune[:1] - re.Flags = p.flags &^ FoldCase - } else if re.Op == OpCharClass && len(re.Rune) == 4 && - re.Rune[0] == re.Rune[1] && re.Rune[2] == re.Rune[3] && - unicode.SimpleFold(re.Rune[0]) == re.Rune[2] && - unicode.SimpleFold(re.Rune[2]) == re.Rune[0] || - re.Op == OpCharClass && len(re.Rune) == 2 && - re.Rune[0]+1 == re.Rune[1] && - unicode.SimpleFold(re.Rune[0]) == re.Rune[1] && - unicode.SimpleFold(re.Rune[1]) == re.Rune[0] { - // Case-insensitive rune like [Aa] or [Δδ]. - if p.maybeConcat(re.Rune[0], p.flags|FoldCase) { - return nil - } - - // Rewrite as (case-insensitive) literal. - re.Op = OpLiteral - re.Rune = re.Rune[:1] - re.Flags = p.flags | FoldCase - } else { - // Incremental concatenation. - p.maybeConcat(-1, 0) - } - - p.stack = append(p.stack, re) - return re -} - -// maybeConcat implements incremental concatenation -// of literal runes into string nodes. The parser calls this -// before each push, so only the top fragment of the stack -// might need processing. Since this is called before a push, -// the topmost literal is no longer subject to operators like * -// (Otherwise ab* would turn into (ab)*.) -// If r >= 0 and there's a node left over, maybeConcat uses it -// to push r with the given flags. -// maybeConcat reports whether r was pushed. -func (p *parser) maybeConcat(r int, flags Flags) bool { - n := len(p.stack) - if n < 2 { - return false - } - - re1 := p.stack[n-1] - re2 := p.stack[n-2] - if re1.Op != OpLiteral || re2.Op != OpLiteral || re1.Flags&FoldCase != re2.Flags&FoldCase { - return false - } - - // Push re1 into re2. - re2.Rune = append(re2.Rune, re1.Rune...) - - // Reuse re1 if possible. - if r >= 0 { - re1.Rune = re1.Rune0[:1] - re1.Rune[0] = r - re1.Flags = flags - return true - } - - p.stack = p.stack[:n-1] - p.reuse(re1) - return false // did not push r -} - -// newLiteral returns a new OpLiteral Regexp with the given flags -func (p *parser) newLiteral(r int, flags Flags) *Regexp { - re := p.newRegexp(OpLiteral) - re.Flags = flags - re.Rune0[0] = r - re.Rune = re.Rune0[:1] - return re -} - -// literal pushes a literal regexp for the rune r on the stack -// and returns that regexp. -func (p *parser) literal(r int) { - p.push(p.newLiteral(r, p.flags)) -} - -// op pushes a regexp with the given op onto the stack -// and returns that regexp. -func (p *parser) op(op Op) *Regexp { - re := p.newRegexp(op) - re.Flags = p.flags - return p.push(re) -} - -// repeat replaces the top stack element with itself repeated -// according to op. -func (p *parser) repeat(op Op, min, max int, opstr, t, lastRepeat string) (string, os.Error) { - flags := p.flags - if p.flags&PerlX != 0 { - if len(t) > 0 && t[0] == '?' { - t = t[1:] - flags ^= NonGreedy - } - if lastRepeat != "" { - // In Perl it is not allowed to stack repetition operators: - // a** is a syntax error, not a doubled star, and a++ means - // something else entirely, which we don't support! - return "", &Error{ErrInvalidRepeatOp, lastRepeat[:len(lastRepeat)-len(t)]} - } - } - n := len(p.stack) - if n == 0 { - return "", &Error{ErrMissingRepeatArgument, opstr} - } - sub := p.stack[n-1] - re := p.newRegexp(op) - re.Min = min - re.Max = max - re.Flags = flags - re.Sub = re.Sub0[:1] - re.Sub[0] = sub - p.stack[n-1] = re - return t, nil -} - -// concat replaces the top of the stack (above the topmost '|' or '(') with its concatenation. -func (p *parser) concat() *Regexp { - p.maybeConcat(-1, 0) - - // Scan down to find pseudo-operator | or (. - i := len(p.stack) - for i > 0 && p.stack[i-1].Op < opPseudo { - i-- - } - subs := p.stack[i:] - p.stack = p.stack[:i] - - // Empty concatenation is special case. - if len(subs) == 0 { - return p.push(p.newRegexp(OpEmptyMatch)) - } - - return p.push(p.collapse(subs, OpConcat)) -} - -// alternate replaces the top of the stack (above the topmost '(') with its alternation. -func (p *parser) alternate() *Regexp { - // Scan down to find pseudo-operator (. - // There are no | above (. - i := len(p.stack) - for i > 0 && p.stack[i-1].Op < opPseudo { - i-- - } - subs := p.stack[i:] - p.stack = p.stack[:i] - - // Make sure top class is clean. - // All the others already are (see swapVerticalBar). - if len(subs) > 0 { - cleanAlt(subs[len(subs)-1]) - } - - // Empty alternate is special case - // (shouldn't happen but easy to handle). - if len(subs) == 0 { - return p.push(p.newRegexp(OpNoMatch)) - } - - return p.push(p.collapse(subs, OpAlternate)) -} - -// cleanAlt cleans re for eventual inclusion in an alternation. -func cleanAlt(re *Regexp) { - switch re.Op { - case OpCharClass: - re.Rune = cleanClass(&re.Rune) - if len(re.Rune) == 2 && re.Rune[0] == 0 && re.Rune[1] == unicode.MaxRune { - re.Rune = nil - re.Op = OpAnyChar - return - } - if len(re.Rune) == 4 && re.Rune[0] == 0 && re.Rune[1] == '\n'-1 && re.Rune[2] == '\n'+1 && re.Rune[3] == unicode.MaxRune { - re.Rune = nil - re.Op = OpAnyCharNotNL - return - } - if cap(re.Rune)-len(re.Rune) > 100 { - // re.Rune will not grow any more. - // Make a copy or inline to reclaim storage. - re.Rune = append(re.Rune0[:0], re.Rune...) - } - } -} - -// collapse returns the result of applying op to sub. -// If sub contains op nodes, they all get hoisted up -// so that there is never a concat of a concat or an -// alternate of an alternate. -func (p *parser) collapse(subs []*Regexp, op Op) *Regexp { - if len(subs) == 1 { - return subs[0] - } - re := p.newRegexp(op) - re.Sub = re.Sub0[:0] - for _, sub := range subs { - if sub.Op == op { - re.Sub = append(re.Sub, sub.Sub...) - p.reuse(sub) - } else { - re.Sub = append(re.Sub, sub) - } - } - if op == OpAlternate { - re.Sub = p.factor(re.Sub, re.Flags) - if len(re.Sub) == 1 { - old := re - re = re.Sub[0] - p.reuse(old) - } - } - return re -} - -// factor factors common prefixes from the alternation list sub. -// It returns a replacement list that reuses the same storage and -// frees (passes to p.reuse) any removed *Regexps. -// -// For example, -// ABC|ABD|AEF|BCX|BCY -// simplifies by literal prefix extraction to -// A(B(C|D)|EF)|BC(X|Y) -// which simplifies by character class introduction to -// A(B[CD]|EF)|BC[XY] -// -func (p *parser) factor(sub []*Regexp, flags Flags) []*Regexp { - if len(sub) < 2 { - return sub - } - - // Round 1: Factor out common literal prefixes. - var str []int - var strflags Flags - start := 0 - out := sub[:0] - for i := 0; i <= len(sub); i++ { - // Invariant: the Regexps that were in sub[0:start] have been - // used or marked for reuse, and the slice space has been reused - // for out (len(out) <= start). - // - // Invariant: sub[start:i] consists of regexps that all begin - // with str as modified by strflags. - var istr []int - var iflags Flags - if i < len(sub) { - istr, iflags = p.leadingString(sub[i]) - if iflags == strflags { - same := 0 - for same < len(str) && same < len(istr) && str[same] == istr[same] { - same++ - } - if same > 0 { - // Matches at least one rune in current range. - // Keep going around. - str = str[:same] - continue - } - } - } - - // Found end of a run with common leading literal string: - // sub[start:i] all begin with str[0:len(str)], but sub[i] - // does not even begin with str[0]. - // - // Factor out common string and append factored expression to out. - if i == start { - // Nothing to do - run of length 0. - } else if i == start+1 { - // Just one: don't bother factoring. - out = append(out, sub[start]) - } else { - // Construct factored form: prefix(suffix1|suffix2|...) - prefix := p.newRegexp(OpLiteral) - prefix.Flags = strflags - prefix.Rune = append(prefix.Rune[:0], str...) - - for j := start; j < i; j++ { - sub[j] = p.removeLeadingString(sub[j], len(str)) - } - suffix := p.collapse(sub[start:i], OpAlternate) // recurse - - re := p.newRegexp(OpConcat) - re.Sub = append(re.Sub[:0], prefix, suffix) - out = append(out, re) - } - - // Prepare for next iteration. - start = i - str = istr - strflags = iflags - } - sub = out - - // Round 2: Factor out common complex prefixes, - // just the first piece of each concatenation, - // whatever it is. This is good enough a lot of the time. - start = 0 - out = sub[:0] - var first *Regexp - for i := 0; i <= len(sub); i++ { - // Invariant: the Regexps that were in sub[0:start] have been - // used or marked for reuse, and the slice space has been reused - // for out (len(out) <= start). - // - // Invariant: sub[start:i] consists of regexps that all begin - // with str as modified by strflags. - var ifirst *Regexp - if i < len(sub) { - ifirst = p.leadingRegexp(sub[i]) - if first != nil && first.Equal(ifirst) { - continue - } - } - - // Found end of a run with common leading regexp: - // sub[start:i] all begin with first but sub[i] does not. - // - // Factor out common regexp and append factored expression to out. - if i == start { - // Nothing to do - run of length 0. - } else if i == start+1 { - // Just one: don't bother factoring. - out = append(out, sub[start]) - } else { - // Construct factored form: prefix(suffix1|suffix2|...) - prefix := first - - for j := start; j < i; j++ { - reuse := j != start // prefix came from sub[start] - sub[j] = p.removeLeadingRegexp(sub[j], reuse) - } - suffix := p.collapse(sub[start:i], OpAlternate) // recurse - - re := p.newRegexp(OpConcat) - re.Sub = append(re.Sub[:0], prefix, suffix) - out = append(out, re) - } - - // Prepare for next iteration. - start = i - first = ifirst - } - sub = out - - // Round 3: Collapse runs of single literals into character classes. - start = 0 - out = sub[:0] - for i := 0; i <= len(sub); i++ { - // Invariant: the Regexps that were in sub[0:start] have been - // used or marked for reuse, and the slice space has been reused - // for out (len(out) <= start). - // - // Invariant: sub[start:i] consists of regexps that are either - // literal runes or character classes. - if i < len(sub) && isCharClass(sub[i]) { - continue - } - - // sub[i] is not a char or char class; - // emit char class for sub[start:i]... - if i == start { - // Nothing to do - run of length 0. - } else if i == start+1 { - out = append(out, sub[start]) - } else { - // Make new char class. - // Start with most complex regexp in sub[start]. - max := start - for j := start + 1; j < i; j++ { - if sub[max].Op < sub[j].Op || sub[max].Op == sub[j].Op && len(sub[max].Rune) < len(sub[j].Rune) { - max = j - } - } - sub[start], sub[max] = sub[max], sub[start] - - for j := start + 1; j < i; j++ { - mergeCharClass(sub[start], sub[j]) - p.reuse(sub[j]) - } - cleanAlt(sub[start]) - out = append(out, sub[start]) - } - - // ... and then emit sub[i]. - if i < len(sub) { - out = append(out, sub[i]) - } - start = i + 1 - } - sub = out - - // Round 4: Collapse runs of empty matches into a single empty match. - start = 0 - out = sub[:0] - for i := range sub { - if i+1 < len(sub) && sub[i].Op == OpEmptyMatch && sub[i+1].Op == OpEmptyMatch { - continue - } - out = append(out, sub[i]) - } - sub = out - - return sub -} - -// leadingString returns the leading literal string that re begins with. -// The string refers to storage in re or its children. -func (p *parser) leadingString(re *Regexp) ([]int, Flags) { - if re.Op == OpConcat && len(re.Sub) > 0 { - re = re.Sub[0] - } - if re.Op != OpLiteral { - return nil, 0 - } - return re.Rune, re.Flags & FoldCase -} - -// removeLeadingString removes the first n leading runes -// from the beginning of re. It returns the replacement for re. -func (p *parser) removeLeadingString(re *Regexp, n int) *Regexp { - if re.Op == OpConcat && len(re.Sub) > 0 { - // Removing a leading string in a concatenation - // might simplify the concatenation. - sub := re.Sub[0] - sub = p.removeLeadingString(sub, n) - re.Sub[0] = sub - if sub.Op == OpEmptyMatch { - p.reuse(sub) - switch len(re.Sub) { - case 0, 1: - // Impossible but handle. - re.Op = OpEmptyMatch - re.Sub = nil - case 2: - old := re - re = re.Sub[1] - p.reuse(old) - default: - copy(re.Sub, re.Sub[1:]) - re.Sub = re.Sub[:len(re.Sub)-1] - } - } - return re - } - - if re.Op == OpLiteral { - re.Rune = re.Rune[:copy(re.Rune, re.Rune[n:])] - if len(re.Rune) == 0 { - re.Op = OpEmptyMatch - } - } - return re -} - -// leadingRegexp returns the leading regexp that re begins with. -// The regexp refers to storage in re or its children. -func (p *parser) leadingRegexp(re *Regexp) *Regexp { - if re.Op == OpEmptyMatch { - return nil - } - if re.Op == OpConcat && len(re.Sub) > 0 { - sub := re.Sub[0] - if sub.Op == OpEmptyMatch { - return nil - } - return sub - } - return re -} - -// removeLeadingRegexp removes the leading regexp in re. -// It returns the replacement for re. -// If reuse is true, it passes the removed regexp (if no longer needed) to p.reuse. -func (p *parser) removeLeadingRegexp(re *Regexp, reuse bool) *Regexp { - if re.Op == OpConcat && len(re.Sub) > 0 { - if reuse { - p.reuse(re.Sub[0]) - } - re.Sub = re.Sub[:copy(re.Sub, re.Sub[1:])] - switch len(re.Sub) { - case 0: - re.Op = OpEmptyMatch - re.Sub = nil - case 1: - old := re - re = re.Sub[0] - p.reuse(old) - } - return re - } - re.Op = OpEmptyMatch - return re -} - -func literalRegexp(s string, flags Flags) *Regexp { - re := &Regexp{Op: OpLiteral} - re.Flags = flags - re.Rune = re.Rune0[:0] // use local storage for small strings - for _, c := range s { - if len(re.Rune) >= cap(re.Rune) { - // string is too long to fit in Rune0. let Go handle it - re.Rune = []int(s) - break - } - re.Rune = append(re.Rune, c) - } - return re -} - -// Parsing. - -func Parse(s string, flags Flags) (*Regexp, os.Error) { - if flags&Literal != 0 { - // Trivial parser for literal string. - if err := checkUTF8(s); err != nil { - return nil, err - } - return literalRegexp(s, flags), nil - } - - // Otherwise, must do real work. - var ( - p parser - err os.Error - c int - op Op - lastRepeat string - min, max int - ) - p.flags = flags - p.wholeRegexp = s - t := s - for t != "" { - repeat := "" - BigSwitch: - switch t[0] { - default: - if c, t, err = nextRune(t); err != nil { - return nil, err - } - p.literal(c) - - case '(': - if p.flags&PerlX != 0 && len(t) >= 2 && t[1] == '?' { - // Flag changes and non-capturing groups. - if t, err = p.parsePerlFlags(t); err != nil { - return nil, err - } - break - } - p.numCap++ - p.op(opLeftParen).Cap = p.numCap - t = t[1:] - case '|': - if err = p.parseVerticalBar(); err != nil { - return nil, err - } - t = t[1:] - case ')': - if err = p.parseRightParen(); err != nil { - return nil, err - } - t = t[1:] - case '^': - if p.flags&OneLine != 0 { - p.op(OpBeginText) - } else { - p.op(OpBeginLine) - } - t = t[1:] - case '$': - if p.flags&OneLine != 0 { - p.op(OpEndText).Flags |= WasDollar - } else { - p.op(OpEndLine) - } - t = t[1:] - case '.': - if p.flags&DotNL != 0 { - p.op(OpAnyChar) - } else { - p.op(OpAnyCharNotNL) - } - t = t[1:] - case '[': - if t, err = p.parseClass(t); err != nil { - return nil, err - } - case '*', '+', '?': - switch t[0] { - case '*': - op = OpStar - case '+': - op = OpPlus - case '?': - op = OpQuest - } - if t, err = p.repeat(op, min, max, t[:1], t[1:], lastRepeat); err != nil { - return nil, err - } - case '{': - op = OpRepeat - min, max, tt, ok := p.parseRepeat(t) - if !ok { - // If the repeat cannot be parsed, { is a literal. - p.literal('{') - t = t[1:] - break - } - if t, err = p.repeat(op, min, max, t[:len(t)-len(tt)], tt, lastRepeat); err != nil { - return nil, err - } - case '\\': - if p.flags&PerlX != 0 && len(t) >= 2 { - switch t[1] { - case 'A': - p.op(OpBeginText) - t = t[2:] - break BigSwitch - case 'b': - p.op(OpWordBoundary) - t = t[2:] - break BigSwitch - case 'B': - p.op(OpNoWordBoundary) - t = t[2:] - break BigSwitch - case 'C': - // any byte; not supported - return nil, &Error{ErrInvalidEscape, t[:2]} - case 'Q': - // \Q ... \E: the ... is always literals - var lit string - if i := strings.Index(t, `\E`); i < 0 { - lit = t[2:] - t = "" - } else { - lit = t[2:i] - t = t[i+2:] - } - p.push(literalRegexp(lit, p.flags)) - break BigSwitch - case 'z': - p.op(OpEndText) - t = t[2:] - break BigSwitch - } - } - - re := p.newRegexp(OpCharClass) - re.Flags = p.flags - - // Look for Unicode character group like \p{Han} - if len(t) >= 2 && (t[1] == 'p' || t[1] == 'P') { - r, rest, err := p.parseUnicodeClass(t, re.Rune0[:0]) - if err != nil { - return nil, err - } - if r != nil { - re.Rune = r - t = rest - p.push(re) - break BigSwitch - } - } - - // Perl character class escape. - if r, rest := p.parsePerlClassEscape(t, re.Rune0[:0]); r != nil { - re.Rune = r - t = rest - p.push(re) - break BigSwitch - } - p.reuse(re) - - // Ordinary single-character escape. - if c, t, err = p.parseEscape(t); err != nil { - return nil, err - } - p.literal(c) - } - lastRepeat = repeat - } - - p.concat() - if p.swapVerticalBar() { - // pop vertical bar - p.stack = p.stack[:len(p.stack)-1] - } - p.alternate() - - n := len(p.stack) - if n != 1 { - return nil, &Error{ErrMissingParen, s} - } - return p.stack[0], nil -} - -// parseRepeat parses {min} (max=min) or {min,} (max=-1) or {min,max}. -// If s is not of that form, it returns ok == false. -func (p *parser) parseRepeat(s string) (min, max int, rest string, ok bool) { - if s == "" || s[0] != '{' { - return - } - s = s[1:] - if min, s, ok = p.parseInt(s); !ok { - return - } - if s == "" { - return - } - if s[0] != ',' { - max = min - } else { - s = s[1:] - if s == "" { - return - } - if s[0] == '}' { - max = -1 - } else if max, s, ok = p.parseInt(s); !ok { - return - } - } - if s == "" || s[0] != '}' { - return - } - rest = s[1:] - ok = true - return -} - -// parsePerlFlags parses a Perl flag setting or non-capturing group or both, -// like (?i) or (?: or (?i:. It removes the prefix from s and updates the parse state. -// The caller must have ensured that s begins with "(?". -func (p *parser) parsePerlFlags(s string) (rest string, err os.Error) { - t := s - - // Check for named captures, first introduced in Python's regexp library. - // As usual, there are three slightly different syntaxes: - // - // (?P<name>expr) the original, introduced by Python - // (?<name>expr) the .NET alteration, adopted by Perl 5.10 - // (?'name'expr) another .NET alteration, adopted by Perl 5.10 - // - // Perl 5.10 gave in and implemented the Python version too, - // but they claim that the last two are the preferred forms. - // PCRE and languages based on it (specifically, PHP and Ruby) - // support all three as well. EcmaScript 4 uses only the Python form. - // - // In both the open source world (via Code Search) and the - // Google source tree, (?P<expr>name) is the dominant form, - // so that's the one we implement. One is enough. - if len(t) > 4 && t[2] == 'P' && t[3] == '<' { - // Pull out name. - end := strings.IndexRune(t, '>') - if end < 0 { - if err = checkUTF8(t); err != nil { - return "", err - } - return "", &Error{ErrInvalidNamedCapture, s} - } - - capture := t[:end+1] // "(?P<name>" - name := t[4:end] // "name" - if err = checkUTF8(name); err != nil { - return "", err - } - if !isValidCaptureName(name) { - return "", &Error{ErrInvalidNamedCapture, capture} - } - - // Like ordinary capture, but named. - p.numCap++ - re := p.op(opLeftParen) - re.Cap = p.numCap - re.Name = name - return t[end+1:], nil - } - - // Non-capturing group. Might also twiddle Perl flags. - var c int - t = t[2:] // skip (? - flags := p.flags - sign := +1 - sawFlag := false -Loop: - for t != "" { - if c, t, err = nextRune(t); err != nil { - return "", err - } - switch c { - default: - break Loop - - // Flags. - case 'i': - flags |= FoldCase - sawFlag = true - case 'm': - flags &^= OneLine - sawFlag = true - case 's': - flags |= DotNL - sawFlag = true - case 'U': - flags |= NonGreedy - sawFlag = true - - // Switch to negation. - case '-': - if sign < 0 { - break Loop - } - sign = -1 - // Invert flags so that | above turn into &^ and vice versa. - // We'll invert flags again before using it below. - flags = ^flags - sawFlag = false - - // End of flags, starting group or not. - case ':', ')': - if sign < 0 { - if !sawFlag { - break Loop - } - flags = ^flags - } - if c == ':' { - // Open new group - p.op(opLeftParen) - } - p.flags = flags - return t, nil - } - } - - return "", &Error{ErrInvalidPerlOp, s[:len(s)-len(t)]} -} - -// isValidCaptureName reports whether name -// is a valid capture name: [A-Za-z0-9_]+. -// PCRE limits names to 32 bytes. -// Python rejects names starting with digits. -// We don't enforce either of those. -func isValidCaptureName(name string) bool { - if name == "" { - return false - } - for _, c := range name { - if c != '_' && !isalnum(c) { - return false - } - } - return true -} - -// parseInt parses a decimal integer. -func (p *parser) parseInt(s string) (n int, rest string, ok bool) { - if s == "" || s[0] < '0' || '9' < s[0] { - return - } - // Disallow leading zeros. - if len(s) >= 2 && s[0] == '0' && '0' <= s[1] && s[1] <= '9' { - return - } - for s != "" && '0' <= s[0] && s[0] <= '9' { - // Avoid overflow. - if n >= 1e8 { - return - } - n = n*10 + int(s[0]) - '0' - s = s[1:] - } - rest = s - ok = true - return -} - -// can this be represented as a character class? -// single-rune literal string, char class, ., and .|\n. -func isCharClass(re *Regexp) bool { - return re.Op == OpLiteral && len(re.Rune) == 1 || - re.Op == OpCharClass || - re.Op == OpAnyCharNotNL || - re.Op == OpAnyChar -} - -// does re match r? -func matchRune(re *Regexp, r int) bool { - switch re.Op { - case OpLiteral: - return len(re.Rune) == 1 && re.Rune[0] == r - case OpCharClass: - for i := 0; i < len(re.Rune); i += 2 { - if re.Rune[i] <= r && r <= re.Rune[i+1] { - return true - } - } - return false - case OpAnyCharNotNL: - return r != '\n' - case OpAnyChar: - return true - } - return false -} - -// parseVerticalBar handles a | in the input. -func (p *parser) parseVerticalBar() os.Error { - p.concat() - - // The concatenation we just parsed is on top of the stack. - // If it sits above an opVerticalBar, swap it below - // (things below an opVerticalBar become an alternation). - // Otherwise, push a new vertical bar. - if !p.swapVerticalBar() { - p.op(opVerticalBar) - } - - return nil -} - -// mergeCharClass makes dst = dst|src. -// The caller must ensure that dst.Op >= src.Op, -// to reduce the amount of copying. -func mergeCharClass(dst, src *Regexp) { - switch dst.Op { - case OpAnyChar: - // src doesn't add anything. - case OpAnyCharNotNL: - // src might add \n - if matchRune(src, '\n') { - dst.Op = OpAnyChar - } - case OpCharClass: - // src is simpler, so either literal or char class - if src.Op == OpLiteral { - dst.Rune = appendRange(dst.Rune, src.Rune[0], src.Rune[0]) - } else { - dst.Rune = appendClass(dst.Rune, src.Rune) - } - case OpLiteral: - // both literal - if src.Rune[0] == dst.Rune[0] { - break - } - dst.Op = OpCharClass - dst.Rune = append(dst.Rune, dst.Rune[0]) - dst.Rune = appendRange(dst.Rune, src.Rune[0], src.Rune[0]) - } -} - -// If the top of the stack is an element followed by an opVerticalBar -// swapVerticalBar swaps the two and returns true. -// Otherwise it returns false. -func (p *parser) swapVerticalBar() bool { - // If above and below vertical bar are literal or char class, - // can merge into a single char class. - n := len(p.stack) - if n >= 3 && p.stack[n-2].Op == opVerticalBar && isCharClass(p.stack[n-1]) && isCharClass(p.stack[n-3]) { - re1 := p.stack[n-1] - re3 := p.stack[n-3] - // Make re3 the more complex of the two. - if re1.Op > re3.Op { - re1, re3 = re3, re1 - p.stack[n-3] = re3 - } - mergeCharClass(re3, re1) - p.reuse(re1) - p.stack = p.stack[:n-1] - return true - } - - if n >= 2 { - re1 := p.stack[n-1] - re2 := p.stack[n-2] - if re2.Op == opVerticalBar { - if n >= 3 { - // Now out of reach. - // Clean opportunistically. - cleanAlt(p.stack[n-3]) - } - p.stack[n-2] = re1 - p.stack[n-1] = re2 - return true - } - } - return false -} - -// parseRightParen handles a ) in the input. -func (p *parser) parseRightParen() os.Error { - p.concat() - if p.swapVerticalBar() { - // pop vertical bar - p.stack = p.stack[:len(p.stack)-1] - } - p.alternate() - - n := len(p.stack) - if n < 2 { - return &Error{ErrInternalError, ""} - } - re1 := p.stack[n-1] - re2 := p.stack[n-2] - p.stack = p.stack[:n-2] - if re2.Op != opLeftParen { - return &Error{ErrMissingParen, p.wholeRegexp} - } - if re2.Cap == 0 { - // Just for grouping. - p.push(re1) - } else { - re2.Op = OpCapture - re2.Sub = re2.Sub0[:1] - re2.Sub[0] = re1 - p.push(re2) - } - return nil -} - -// parseEscape parses an escape sequence at the beginning of s -// and returns the rune. -func (p *parser) parseEscape(s string) (r int, rest string, err os.Error) { - t := s[1:] - if t == "" { - return 0, "", &Error{ErrTrailingBackslash, ""} - } - c, t, err := nextRune(t) - if err != nil { - return 0, "", err - } - -Switch: - switch c { - default: - if c < utf8.RuneSelf && !isalnum(c) { - // Escaped non-word characters are always themselves. - // PCRE is not quite so rigorous: it accepts things like - // \q, but we don't. We once rejected \_, but too many - // programs and people insist on using it, so allow \_. - return c, t, nil - } - - // Octal escapes. - case '1', '2', '3', '4', '5', '6', '7': - // Single non-zero digit is a backreference; not supported - if t == "" || t[0] < '0' || t[0] > '7' { - break - } - fallthrough - case '0': - // Consume up to three octal digits; already have one. - r = c - '0' - for i := 1; i < 3; i++ { - if t == "" || t[0] < '0' || t[0] > '7' { - break - } - r = r*8 + int(t[0]) - '0' - t = t[1:] - } - return r, t, nil - - // Hexadecimal escapes. - case 'x': - if t == "" { - break - } - if c, t, err = nextRune(t); err != nil { - return 0, "", err - } - if c == '{' { - // Any number of digits in braces. - // Perl accepts any text at all; it ignores all text - // after the first non-hex digit. We require only hex digits, - // and at least one. - nhex := 0 - r = 0 - for { - if t == "" { - break Switch - } - if c, t, err = nextRune(t); err != nil { - return 0, "", err - } - if c == '}' { - break - } - v := unhex(c) - if v < 0 { - break Switch - } - r = r*16 + v - if r > unicode.MaxRune { - break Switch - } - nhex++ - } - if nhex == 0 { - break Switch - } - return r, t, nil - } - - // Easy case: two hex digits. - x := unhex(c) - if c, t, err = nextRune(t); err != nil { - return 0, "", err - } - y := unhex(c) - if x < 0 || y < 0 { - break - } - return x*16 + y, t, nil - - // C escapes. There is no case 'b', to avoid misparsing - // the Perl word-boundary \b as the C backspace \b - // when in POSIX mode. In Perl, /\b/ means word-boundary - // but /[\b]/ means backspace. We don't support that. - // If you want a backspace, embed a literal backspace - // character or use \x08. - case 'a': - return '\a', t, err - case 'f': - return '\f', t, err - case 'n': - return '\n', t, err - case 'r': - return '\r', t, err - case 't': - return '\t', t, err - case 'v': - return '\v', t, err - } - return 0, "", &Error{ErrInvalidEscape, s[:len(s)-len(t)]} -} - -// parseClassChar parses a character class character at the beginning of s -// and returns it. -func (p *parser) parseClassChar(s, wholeClass string) (r int, rest string, err os.Error) { - if s == "" { - return 0, "", &Error{Code: ErrMissingBracket, Expr: wholeClass} - } - - // Allow regular escape sequences even though - // many need not be escaped in this context. - if s[0] == '\\' { - return p.parseEscape(s) - } - - return nextRune(s) -} - -type charGroup struct { - sign int - class []int -} - -// parsePerlClassEscape parses a leading Perl character class escape like \d -// from the beginning of s. If one is present, it appends the characters to r -// and returns the new slice r and the remainder of the string. -func (p *parser) parsePerlClassEscape(s string, r []int) (out []int, rest string) { - if p.flags&PerlX == 0 || len(s) < 2 || s[0] != '\\' { - return - } - g := perlGroup[s[0:2]] - if g.sign == 0 { - return - } - return p.appendGroup(r, g), s[2:] -} - -// parseNamedClass parses a leading POSIX named character class like [:alnum:] -// from the beginning of s. If one is present, it appends the characters to r -// and returns the new slice r and the remainder of the string. -func (p *parser) parseNamedClass(s string, r []int) (out []int, rest string, err os.Error) { - if len(s) < 2 || s[0] != '[' || s[1] != ':' { - return - } - - i := strings.Index(s[2:], ":]") - if i < 0 { - return - } - i += 2 - name, s := s[0:i+2], s[i+2:] - g := posixGroup[name] - if g.sign == 0 { - return nil, "", &Error{ErrInvalidCharRange, name} - } - return p.appendGroup(r, g), s, nil -} - -func (p *parser) appendGroup(r []int, g charGroup) []int { - if p.flags&FoldCase == 0 { - if g.sign < 0 { - r = appendNegatedClass(r, g.class) - } else { - r = appendClass(r, g.class) - } - } else { - tmp := p.tmpClass[:0] - tmp = appendFoldedClass(tmp, g.class) - p.tmpClass = tmp - tmp = cleanClass(&p.tmpClass) - if g.sign < 0 { - r = appendNegatedClass(r, tmp) - } else { - r = appendClass(r, tmp) - } - } - return r -} - -// unicodeTable returns the unicode.RangeTable identified by name -// and the table of additional fold-equivalent code points. -func unicodeTable(name string) (*unicode.RangeTable, *unicode.RangeTable) { - if t := unicode.Categories[name]; t != nil { - return t, unicode.FoldCategory[name] - } - if t := unicode.Scripts[name]; t != nil { - return t, unicode.FoldScript[name] - } - return nil, nil -} - -// parseUnicodeClass parses a leading Unicode character class like \p{Han} -// from the beginning of s. If one is present, it appends the characters to r -// and returns the new slice r and the remainder of the string. -func (p *parser) parseUnicodeClass(s string, r []int) (out []int, rest string, err os.Error) { - if p.flags&UnicodeGroups == 0 || len(s) < 2 || s[0] != '\\' || s[1] != 'p' && s[1] != 'P' { - return - } - - // Committed to parse or return error. - sign := +1 - if s[1] == 'P' { - sign = -1 - } - t := s[2:] - c, t, err := nextRune(t) - if err != nil { - return - } - var seq, name string - if c != '{' { - // Single-letter name. - seq = s[:len(s)-len(t)] - name = seq[2:] - } else { - // Name is in braces. - end := strings.IndexRune(s, '}') - if end < 0 { - if err = checkUTF8(s); err != nil { - return - } - return nil, "", &Error{ErrInvalidCharRange, s} - } - seq, t = s[:end+1], s[end+1:] - name = s[3:end] - if err = checkUTF8(name); err != nil { - return - } - } - - // Group can have leading negation too. \p{^Han} == \P{Han}, \P{^Han} == \p{Han}. - if name != "" && name[0] == '^' { - sign = -sign - name = name[1:] - } - - tab, fold := unicodeTable(name) - if tab == nil { - return nil, "", &Error{ErrInvalidCharRange, seq} - } - - if p.flags&FoldCase == 0 || fold == nil { - if sign > 0 { - r = appendTable(r, tab) - } else { - r = appendNegatedTable(r, tab) - } - } else { - // Merge and clean tab and fold in a temporary buffer. - // This is necessary for the negative case and just tidy - // for the positive case. - tmp := p.tmpClass[:0] - tmp = appendTable(tmp, tab) - tmp = appendTable(tmp, fold) - p.tmpClass = tmp - tmp = cleanClass(&p.tmpClass) - if sign > 0 { - r = appendClass(r, tmp) - } else { - r = appendNegatedClass(r, tmp) - } - } - return r, t, nil -} - -// parseClass parses a character class at the beginning of s -// and pushes it onto the parse stack. -func (p *parser) parseClass(s string) (rest string, err os.Error) { - t := s[1:] // chop [ - re := p.newRegexp(OpCharClass) - re.Flags = p.flags - re.Rune = re.Rune0[:0] - - sign := +1 - if t != "" && t[0] == '^' { - sign = -1 - t = t[1:] - - // If character class does not match \n, add it here, - // so that negation later will do the right thing. - if p.flags&ClassNL == 0 { - re.Rune = append(re.Rune, '\n', '\n') - } - } - - class := re.Rune - first := true // ] and - are okay as first char in class - for t == "" || t[0] != ']' || first { - // POSIX: - is only okay unescaped as first or last in class. - // Perl: - is okay anywhere. - if t != "" && t[0] == '-' && p.flags&PerlX == 0 && !first && (len(t) == 1 || t[1] != ']') { - _, size := utf8.DecodeRuneInString(t[1:]) - return "", &Error{Code: ErrInvalidCharRange, Expr: t[:1+size]} - } - first = false - - // Look for POSIX [:alnum:] etc. - if len(t) > 2 && t[0] == '[' && t[1] == ':' { - nclass, nt, err := p.parseNamedClass(t, class) - if err != nil { - return "", err - } - if nclass != nil { - class, t = nclass, nt - continue - } - } - - // Look for Unicode character group like \p{Han}. - nclass, nt, err := p.parseUnicodeClass(t, class) - if err != nil { - return "", err - } - if nclass != nil { - class, t = nclass, nt - continue - } - - // Look for Perl character class symbols (extension). - if nclass, nt := p.parsePerlClassEscape(t, class); nclass != nil { - class, t = nclass, nt - continue - } - - // Single character or simple range. - rng := t - var lo, hi int - if lo, t, err = p.parseClassChar(t, s); err != nil { - return "", err - } - hi = lo - // [a-] means (a|-) so check for final ]. - if len(t) >= 2 && t[0] == '-' && t[1] != ']' { - t = t[1:] - if hi, t, err = p.parseClassChar(t, s); err != nil { - return "", err - } - if hi < lo { - rng = rng[:len(rng)-len(t)] - return "", &Error{Code: ErrInvalidCharRange, Expr: rng} - } - } - if p.flags&FoldCase == 0 { - class = appendRange(class, lo, hi) - } else { - class = appendFoldedRange(class, lo, hi) - } - } - t = t[1:] // chop ] - - // Use &re.Rune instead of &class to avoid allocation. - re.Rune = class - class = cleanClass(&re.Rune) - if sign < 0 { - class = negateClass(class) - } - re.Rune = class - p.push(re) - return t, nil -} - -// cleanClass sorts the ranges (pairs of elements of r), -// merges them, and eliminates duplicates. -func cleanClass(rp *[]int) []int { - - // Sort by lo increasing, hi decreasing to break ties. - sort.Sort(ranges{rp}) - - r := *rp - if len(r) < 2 { - return r - } - - // Merge abutting, overlapping. - w := 2 // write index - for i := 2; i < len(r); i += 2 { - lo, hi := r[i], r[i+1] - if lo <= r[w-1]+1 { - // merge with previous range - if hi > r[w-1] { - r[w-1] = hi - } - continue - } - // new disjoint range - r[w] = lo - r[w+1] = hi - w += 2 - } - - return r[:w] -} - -// appendRange returns the result of appending the range lo-hi to the class r. -func appendRange(r []int, lo, hi int) []int { - // Expand last range or next to last range if it overlaps or abuts. - // Checking two ranges helps when appending case-folded - // alphabets, so that one range can be expanding A-Z and the - // other expanding a-z. - n := len(r) - for i := 2; i <= 4; i += 2 { // twice, using i=2, i=4 - if n >= i { - rlo, rhi := r[n-i], r[n-i+1] - if lo <= rhi+1 && rlo <= hi+1 { - if lo < rlo { - r[n-i] = lo - } - if hi > rhi { - r[n-i+1] = hi - } - return r - } - } - } - - return append(r, lo, hi) -} - -const ( - // minimum and maximum runes involved in folding. - // checked during test. - minFold = 0x0041 - maxFold = 0x1044f -) - -// appendFoldedRange returns the result of appending the range lo-hi -// and its case folding-equivalent runes to the class r. -func appendFoldedRange(r []int, lo, hi int) []int { - // Optimizations. - if lo <= minFold && hi >= maxFold { - // Range is full: folding can't add more. - return appendRange(r, lo, hi) - } - if hi < minFold || lo > maxFold { - // Range is outside folding possibilities. - return appendRange(r, lo, hi) - } - if lo < minFold { - // [lo, minFold-1] needs no folding. - r = appendRange(r, lo, minFold-1) - lo = minFold - } - if hi > maxFold { - // [maxFold+1, hi] needs no folding. - r = appendRange(r, maxFold+1, hi) - hi = maxFold - } - - // Brute force. Depend on appendRange to coalesce ranges on the fly. - for c := lo; c <= hi; c++ { - r = appendRange(r, c, c) - f := unicode.SimpleFold(c) - for f != c { - r = appendRange(r, f, f) - f = unicode.SimpleFold(f) - } - } - return r -} - -// appendClass returns the result of appending the class x to the class r. -// It assume x is clean. -func appendClass(r []int, x []int) []int { - for i := 0; i < len(x); i += 2 { - r = appendRange(r, x[i], x[i+1]) - } - return r -} - -// appendFolded returns the result of appending the case folding of the class x to the class r. -func appendFoldedClass(r []int, x []int) []int { - for i := 0; i < len(x); i += 2 { - r = appendFoldedRange(r, x[i], x[i+1]) - } - return r -} - -// appendNegatedClass returns the result of appending the negation of the class x to the class r. -// It assumes x is clean. -func appendNegatedClass(r []int, x []int) []int { - nextLo := 0 - for i := 0; i < len(x); i += 2 { - lo, hi := x[i], x[i+1] - if nextLo <= lo-1 { - r = appendRange(r, nextLo, lo-1) - } - nextLo = hi + 1 - } - if nextLo <= unicode.MaxRune { - r = appendRange(r, nextLo, unicode.MaxRune) - } - return r -} - -// appendTable returns the result of appending x to the class r. -func appendTable(r []int, x *unicode.RangeTable) []int { - for _, xr := range x.R16 { - lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) - if stride == 1 { - r = appendRange(r, lo, hi) - continue - } - for c := lo; c <= hi; c += stride { - r = appendRange(r, c, c) - } - } - for _, xr := range x.R32 { - lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) - if stride == 1 { - r = appendRange(r, lo, hi) - continue - } - for c := lo; c <= hi; c += stride { - r = appendRange(r, c, c) - } - } - return r -} - -// appendNegatedTable returns the result of appending the negation of x to the class r. -func appendNegatedTable(r []int, x *unicode.RangeTable) []int { - nextLo := 0 // lo end of next class to add - for _, xr := range x.R16 { - lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) - if stride == 1 { - if nextLo <= lo-1 { - r = appendRange(r, nextLo, lo-1) - } - nextLo = hi + 1 - continue - } - for c := lo; c <= hi; c += stride { - if nextLo <= c-1 { - r = appendRange(r, nextLo, c-1) - } - nextLo = c + 1 - } - } - for _, xr := range x.R32 { - lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) - if stride == 1 { - if nextLo <= lo-1 { - r = appendRange(r, nextLo, lo-1) - } - nextLo = hi + 1 - continue - } - for c := lo; c <= hi; c += stride { - if nextLo <= c-1 { - r = appendRange(r, nextLo, c-1) - } - nextLo = c + 1 - } - } - if nextLo <= unicode.MaxRune { - r = appendRange(r, nextLo, unicode.MaxRune) - } - return r -} - -// negateClass overwrites r and returns r's negation. -// It assumes the class r is already clean. -func negateClass(r []int) []int { - nextLo := 0 // lo end of next class to add - w := 0 // write index - for i := 0; i < len(r); i += 2 { - lo, hi := r[i], r[i+1] - if nextLo <= lo-1 { - r[w] = nextLo - r[w+1] = lo - 1 - w += 2 - } - nextLo = hi + 1 - } - r = r[:w] - if nextLo <= unicode.MaxRune { - // It's possible for the negation to have one more - // range - this one - than the original class, so use append. - r = append(r, nextLo, unicode.MaxRune) - } - return r -} - -// ranges implements sort.Interface on a []rune. -// The choice of receiver type definition is strange -// but avoids an allocation since we already have -// a *[]int. -type ranges struct { - p *[]int -} - -func (ra ranges) Less(i, j int) bool { - p := *ra.p - i *= 2 - j *= 2 - return p[i] < p[j] || p[i] == p[j] && p[i+1] > p[j+1] -} - -func (ra ranges) Len() int { - return len(*ra.p) / 2 -} - -func (ra ranges) Swap(i, j int) { - p := *ra.p - i *= 2 - j *= 2 - p[i], p[i+1], p[j], p[j+1] = p[j], p[j+1], p[i], p[i+1] -} - -func checkUTF8(s string) os.Error { - for s != "" { - rune, size := utf8.DecodeRuneInString(s) - if rune == utf8.RuneError && size == 1 { - return &Error{Code: ErrInvalidUTF8, Expr: s} - } - s = s[size:] - } - return nil -} - -func nextRune(s string) (c int, t string, err os.Error) { - c, size := utf8.DecodeRuneInString(s) - if c == utf8.RuneError && size == 1 { - return 0, "", &Error{Code: ErrInvalidUTF8, Expr: s} - } - return c, s[size:], nil -} - -func isalnum(c int) bool { - return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' -} - -func unhex(c int) int { - if '0' <= c && c <= '9' { - return c - '0' - } - if 'a' <= c && c <= 'f' { - return c - 'a' + 10 - } - if 'A' <= c && c <= 'F' { - return c - 'A' + 10 - } - return -1 -} diff --git a/libgo/go/exp/regexp/syntax/parse_test.go b/libgo/go/exp/regexp/syntax/parse_test.go deleted file mode 100644 index 779b9afdeae..00000000000 --- a/libgo/go/exp/regexp/syntax/parse_test.go +++ /dev/null @@ -1,350 +0,0 @@ -// 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 syntax - -import ( - "bytes" - "fmt" - "testing" - "unicode" -) - -var parseTests = []struct { - Regexp string - Dump string -}{ - // Base cases - {`a`, `lit{a}`}, - {`a.`, `cat{lit{a}dot{}}`}, - {`a.b`, `cat{lit{a}dot{}lit{b}}`}, - {`ab`, `str{ab}`}, - {`a.b.c`, `cat{lit{a}dot{}lit{b}dot{}lit{c}}`}, - {`abc`, `str{abc}`}, - {`a|^`, `alt{lit{a}bol{}}`}, - {`a|b`, `cc{0x61-0x62}`}, - {`(a)`, `cap{lit{a}}`}, - {`(a)|b`, `alt{cap{lit{a}}lit{b}}`}, - {`a*`, `star{lit{a}}`}, - {`a+`, `plus{lit{a}}`}, - {`a?`, `que{lit{a}}`}, - {`a{2}`, `rep{2,2 lit{a}}`}, - {`a{2,3}`, `rep{2,3 lit{a}}`}, - {`a{2,}`, `rep{2,-1 lit{a}}`}, - {`a*?`, `nstar{lit{a}}`}, - {`a+?`, `nplus{lit{a}}`}, - {`a??`, `nque{lit{a}}`}, - {`a{2}?`, `nrep{2,2 lit{a}}`}, - {`a{2,3}?`, `nrep{2,3 lit{a}}`}, - {`a{2,}?`, `nrep{2,-1 lit{a}}`}, - {``, `emp{}`}, - {`|`, `emp{}`}, // alt{emp{}emp{}} but got factored - {`|x|`, `alt{emp{}lit{x}emp{}}`}, - {`.`, `dot{}`}, - {`^`, `bol{}`}, - {`$`, `eol{}`}, - {`\|`, `lit{|}`}, - {`\(`, `lit{(}`}, - {`\)`, `lit{)}`}, - {`\*`, `lit{*}`}, - {`\+`, `lit{+}`}, - {`\?`, `lit{?}`}, - {`{`, `lit{{}`}, - {`}`, `lit{}}`}, - {`\.`, `lit{.}`}, - {`\^`, `lit{^}`}, - {`\$`, `lit{$}`}, - {`\\`, `lit{\}`}, - {`[ace]`, `cc{0x61 0x63 0x65}`}, - {`[abc]`, `cc{0x61-0x63}`}, - {`[a-z]`, `cc{0x61-0x7a}`}, - {`[a]`, `lit{a}`}, - {`\-`, `lit{-}`}, - {`-`, `lit{-}`}, - {`\_`, `lit{_}`}, - {`abc`, `str{abc}`}, - {`abc|def`, `alt{str{abc}str{def}}`}, - {`abc|def|ghi`, `alt{str{abc}str{def}str{ghi}}`}, - - // Posix and Perl extensions - {`[[:lower:]]`, `cc{0x61-0x7a}`}, - {`[a-z]`, `cc{0x61-0x7a}`}, - {`[^[:lower:]]`, `cc{0x0-0x60 0x7b-0x10ffff}`}, - {`[[:^lower:]]`, `cc{0x0-0x60 0x7b-0x10ffff}`}, - {`(?i)[[:lower:]]`, `cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}`}, - {`(?i)[a-z]`, `cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}`}, - {`(?i)[^[:lower:]]`, `cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`}, - {`(?i)[[:^lower:]]`, `cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`}, - {`\d`, `cc{0x30-0x39}`}, - {`\D`, `cc{0x0-0x2f 0x3a-0x10ffff}`}, - {`\s`, `cc{0x9-0xa 0xc-0xd 0x20}`}, - {`\S`, `cc{0x0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}`}, - {`\w`, `cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a}`}, - {`\W`, `cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x10ffff}`}, - {`(?i)\w`, `cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a 0x17f 0x212a}`}, - {`(?i)\W`, `cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`}, - {`[^\\]`, `cc{0x0-0x5b 0x5d-0x10ffff}`}, - // { `\C`, `byte{}` }, // probably never - - // Unicode, negatives, and a double negative. - {`\p{Braille}`, `cc{0x2800-0x28ff}`}, - {`\P{Braille}`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, - {`\p{^Braille}`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, - {`\P{^Braille}`, `cc{0x2800-0x28ff}`}, - {`\pZ`, `cc{0x20 0xa0 0x1680 0x180e 0x2000-0x200a 0x2028-0x2029 0x202f 0x205f 0x3000}`}, - {`[\p{Braille}]`, `cc{0x2800-0x28ff}`}, - {`[\P{Braille}]`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, - {`[\p{^Braille}]`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, - {`[\P{^Braille}]`, `cc{0x2800-0x28ff}`}, - {`[\pZ]`, `cc{0x20 0xa0 0x1680 0x180e 0x2000-0x200a 0x2028-0x2029 0x202f 0x205f 0x3000}`}, - {`\p{Lu}`, mkCharClass(unicode.IsUpper)}, - {`[\p{Lu}]`, mkCharClass(unicode.IsUpper)}, - {`(?i)[\p{Lu}]`, mkCharClass(isUpperFold)}, - - // Hex, octal. - {`[\012-\234]\141`, `cat{cc{0xa-0x9c}lit{a}}`}, - {`[\x{41}-\x7a]\x61`, `cat{cc{0x41-0x7a}lit{a}}`}, - - // More interesting regular expressions. - {`a{,2}`, `str{a{,2}}`}, - {`\.\^\$\\`, `str{.^$\}`}, - {`[a-zABC]`, `cc{0x41-0x43 0x61-0x7a}`}, - {`[^a]`, `cc{0x0-0x60 0x62-0x10ffff}`}, - {`[α-ε☺]`, `cc{0x3b1-0x3b5 0x263a}`}, // utf-8 - {`a*{`, `cat{star{lit{a}}lit{{}}`}, - - // Test precedences - {`(?:ab)*`, `star{str{ab}}`}, - {`(ab)*`, `star{cap{str{ab}}}`}, - {`ab|cd`, `alt{str{ab}str{cd}}`}, - {`a(b|c)d`, `cat{lit{a}cap{cc{0x62-0x63}}lit{d}}`}, - - // Test flattening. - {`(?:a)`, `lit{a}`}, - {`(?:ab)(?:cd)`, `str{abcd}`}, - {`(?:a+b+)(?:c+d+)`, `cat{plus{lit{a}}plus{lit{b}}plus{lit{c}}plus{lit{d}}}`}, - {`(?:a+|b+)|(?:c+|d+)`, `alt{plus{lit{a}}plus{lit{b}}plus{lit{c}}plus{lit{d}}}`}, - {`(?:a|b)|(?:c|d)`, `cc{0x61-0x64}`}, - {`a|.`, `dot{}`}, - {`.|a`, `dot{}`}, - {`(?:[abc]|A|Z|hello|world)`, `alt{cc{0x41 0x5a 0x61-0x63}str{hello}str{world}}`}, - {`(?:[abc]|A|Z)`, `cc{0x41 0x5a 0x61-0x63}`}, - - // Test Perl quoted literals - {`\Q+|*?{[\E`, `str{+|*?{[}`}, - {`\Q+\E+`, `plus{lit{+}}`}, - {`\Q\\E`, `lit{\}`}, - {`\Q\\\E`, `str{\\}`}, - - // Test Perl \A and \z - {`(?m)^`, `bol{}`}, - {`(?m)$`, `eol{}`}, - {`(?-m)^`, `bot{}`}, - {`(?-m)$`, `eot{}`}, - {`(?m)\A`, `bot{}`}, - {`(?m)\z`, `eot{\z}`}, - {`(?-m)\A`, `bot{}`}, - {`(?-m)\z`, `eot{\z}`}, - - // Test named captures - {`(?P<name>a)`, `cap{name:lit{a}}`}, - - // Case-folded literals - {`[Aa]`, `litfold{A}`}, - {`[\x{100}\x{101}]`, `litfold{Ā}`}, - {`[Δδ]`, `litfold{Δ}`}, - - // Strings - {`abcde`, `str{abcde}`}, - {`[Aa][Bb]cd`, `cat{strfold{AB}str{cd}}`}, - - // Factoring. - {`abc|abd|aef|bcx|bcy`, `alt{cat{lit{a}alt{cat{lit{b}cc{0x63-0x64}}str{ef}}}cat{str{bc}cc{0x78-0x79}}}`}, - {`ax+y|ax+z|ay+w`, `cat{lit{a}alt{cat{plus{lit{x}}cc{0x79-0x7a}}cat{plus{lit{y}}lit{w}}}}`}, -} - -const testFlags = MatchNL | PerlX | UnicodeGroups - -// Test Parse -> Dump. -func TestParseDump(t *testing.T) { - for _, tt := range parseTests { - re, err := Parse(tt.Regexp, testFlags) - if err != nil { - t.Errorf("Parse(%#q): %v", tt.Regexp, err) - continue - } - d := dump(re) - if d != tt.Dump { - t.Errorf("Parse(%#q).Dump() = %#q want %#q", tt.Regexp, d, tt.Dump) - } - } -} - -// dump prints a string representation of the regexp showing -// the structure explicitly. -func dump(re *Regexp) string { - var b bytes.Buffer - dumpRegexp(&b, re) - return b.String() -} - -var opNames = []string{ - OpNoMatch: "no", - OpEmptyMatch: "emp", - OpLiteral: "lit", - OpCharClass: "cc", - OpAnyCharNotNL: "dnl", - OpAnyChar: "dot", - OpBeginLine: "bol", - OpEndLine: "eol", - OpBeginText: "bot", - OpEndText: "eot", - OpWordBoundary: "wb", - OpNoWordBoundary: "nwb", - OpCapture: "cap", - OpStar: "star", - OpPlus: "plus", - OpQuest: "que", - OpRepeat: "rep", - OpConcat: "cat", - OpAlternate: "alt", -} - -// dumpRegexp writes an encoding of the syntax tree for the regexp re to b. -// It is used during testing to distinguish between parses that might print -// the same using re's String method. -func dumpRegexp(b *bytes.Buffer, re *Regexp) { - if int(re.Op) >= len(opNames) || opNames[re.Op] == "" { - fmt.Fprintf(b, "op%d", re.Op) - } else { - switch re.Op { - default: - b.WriteString(opNames[re.Op]) - case OpStar, OpPlus, OpQuest, OpRepeat: - if re.Flags&NonGreedy != 0 { - b.WriteByte('n') - } - b.WriteString(opNames[re.Op]) - case OpLiteral: - if len(re.Rune) > 1 { - b.WriteString("str") - } else { - b.WriteString("lit") - } - if re.Flags&FoldCase != 0 { - for _, r := range re.Rune { - if unicode.SimpleFold(r) != r { - b.WriteString("fold") - break - } - } - } - } - } - b.WriteByte('{') - switch re.Op { - case OpEndText: - if re.Flags&WasDollar == 0 { - b.WriteString(`\z`) - } - case OpLiteral: - for _, r := range re.Rune { - b.WriteRune(r) - } - case OpConcat, OpAlternate: - for _, sub := range re.Sub { - dumpRegexp(b, sub) - } - case OpStar, OpPlus, OpQuest: - dumpRegexp(b, re.Sub[0]) - case OpRepeat: - fmt.Fprintf(b, "%d,%d ", re.Min, re.Max) - dumpRegexp(b, re.Sub[0]) - case OpCapture: - if re.Name != "" { - b.WriteString(re.Name) - b.WriteByte(':') - } - dumpRegexp(b, re.Sub[0]) - case OpCharClass: - sep := "" - for i := 0; i < len(re.Rune); i += 2 { - b.WriteString(sep) - sep = " " - lo, hi := re.Rune[i], re.Rune[i+1] - if lo == hi { - fmt.Fprintf(b, "%#x", lo) - } else { - fmt.Fprintf(b, "%#x-%#x", lo, hi) - } - } - } - b.WriteByte('}') -} - -func mkCharClass(f func(int) bool) string { - re := &Regexp{Op: OpCharClass} - lo := -1 - for i := 0; i <= unicode.MaxRune; i++ { - if f(i) { - if lo < 0 { - lo = i - } - } else { - if lo >= 0 { - re.Rune = append(re.Rune, lo, i-1) - lo = -1 - } - } - } - if lo >= 0 { - re.Rune = append(re.Rune, lo, unicode.MaxRune) - } - return dump(re) -} - -func isUpperFold(rune int) bool { - if unicode.IsUpper(rune) { - return true - } - c := unicode.SimpleFold(rune) - for c != rune { - if unicode.IsUpper(c) { - return true - } - c = unicode.SimpleFold(c) - } - return false -} - -func TestFoldConstants(t *testing.T) { - last := -1 - for i := 0; i <= unicode.MaxRune; i++ { - if unicode.SimpleFold(i) == i { - continue - } - if last == -1 && minFold != i { - t.Errorf("minFold=%#U should be %#U", minFold, i) - } - last = i - } - if maxFold != last { - t.Errorf("maxFold=%#U should be %#U", maxFold, last) - } -} - -func TestAppendRangeCollapse(t *testing.T) { - // AppendRange should collapse each of the new ranges - // into the earlier ones (it looks back two ranges), so that - // the slice never grows very large. - // Note that we are not calling cleanClass. - var r []int - for i := 'A'; i <= 'Z'; i++ { - r = appendRange(r, i, i) - r = appendRange(r, i+'a'-'A', i+'a'-'A') - } - if string(r) != "AZaz" { - t.Errorf("appendRange interlaced A-Z a-z = %s, want AZaz", string(r)) - } -} diff --git a/libgo/go/exp/regexp/syntax/perl_groups.go b/libgo/go/exp/regexp/syntax/perl_groups.go deleted file mode 100644 index 05b392c40d8..00000000000 --- a/libgo/go/exp/regexp/syntax/perl_groups.go +++ /dev/null @@ -1,130 +0,0 @@ -// GENERATED BY make_perl_groups.pl; DO NOT EDIT. -// make_perl_groups.pl >perl_groups.go - -package syntax - -var code1 = []int{ /* \d */ - 0x30, 0x39, -} - -var code2 = []int{ /* \s */ - 0x9, 0xa, - 0xc, 0xd, - 0x20, 0x20, -} - -var code3 = []int{ /* \w */ - 0x30, 0x39, - 0x41, 0x5a, - 0x5f, 0x5f, - 0x61, 0x7a, -} - -var perlGroup = map[string]charGroup{ - `\d`: {+1, code1}, - `\D`: {-1, code1}, - `\s`: {+1, code2}, - `\S`: {-1, code2}, - `\w`: {+1, code3}, - `\W`: {-1, code3}, -} -var code4 = []int{ /* [:alnum:] */ - 0x30, 0x39, - 0x41, 0x5a, - 0x61, 0x7a, -} - -var code5 = []int{ /* [:alpha:] */ - 0x41, 0x5a, - 0x61, 0x7a, -} - -var code6 = []int{ /* [:ascii:] */ - 0x0, 0x7f, -} - -var code7 = []int{ /* [:blank:] */ - 0x9, 0x9, - 0x20, 0x20, -} - -var code8 = []int{ /* [:cntrl:] */ - 0x0, 0x1f, - 0x7f, 0x7f, -} - -var code9 = []int{ /* [:digit:] */ - 0x30, 0x39, -} - -var code10 = []int{ /* [:graph:] */ - 0x21, 0x7e, -} - -var code11 = []int{ /* [:lower:] */ - 0x61, 0x7a, -} - -var code12 = []int{ /* [:print:] */ - 0x20, 0x7e, -} - -var code13 = []int{ /* [:punct:] */ - 0x21, 0x2f, - 0x3a, 0x40, - 0x5b, 0x60, - 0x7b, 0x7e, -} - -var code14 = []int{ /* [:space:] */ - 0x9, 0xd, - 0x20, 0x20, -} - -var code15 = []int{ /* [:upper:] */ - 0x41, 0x5a, -} - -var code16 = []int{ /* [:word:] */ - 0x30, 0x39, - 0x41, 0x5a, - 0x5f, 0x5f, - 0x61, 0x7a, -} - -var code17 = []int{ /* [:xdigit:] */ - 0x30, 0x39, - 0x41, 0x46, - 0x61, 0x66, -} - -var posixGroup = map[string]charGroup{ - `[:alnum:]`: {+1, code4}, - `[:^alnum:]`: {-1, code4}, - `[:alpha:]`: {+1, code5}, - `[:^alpha:]`: {-1, code5}, - `[:ascii:]`: {+1, code6}, - `[:^ascii:]`: {-1, code6}, - `[:blank:]`: {+1, code7}, - `[:^blank:]`: {-1, code7}, - `[:cntrl:]`: {+1, code8}, - `[:^cntrl:]`: {-1, code8}, - `[:digit:]`: {+1, code9}, - `[:^digit:]`: {-1, code9}, - `[:graph:]`: {+1, code10}, - `[:^graph:]`: {-1, code10}, - `[:lower:]`: {+1, code11}, - `[:^lower:]`: {-1, code11}, - `[:print:]`: {+1, code12}, - `[:^print:]`: {-1, code12}, - `[:punct:]`: {+1, code13}, - `[:^punct:]`: {-1, code13}, - `[:space:]`: {+1, code14}, - `[:^space:]`: {-1, code14}, - `[:upper:]`: {+1, code15}, - `[:^upper:]`: {-1, code15}, - `[:word:]`: {+1, code16}, - `[:^word:]`: {-1, code16}, - `[:xdigit:]`: {+1, code17}, - `[:^xdigit:]`: {-1, code17}, -} diff --git a/libgo/go/exp/regexp/syntax/prog.go b/libgo/go/exp/regexp/syntax/prog.go deleted file mode 100644 index bf85b720d02..00000000000 --- a/libgo/go/exp/regexp/syntax/prog.go +++ /dev/null @@ -1,237 +0,0 @@ -package syntax - -import ( - "bytes" - "strconv" -) - -// Compiled program. -// May not belong in this package, but convenient for now. - -// A Prog is a compiled regular expression program. -type Prog struct { - Inst []Inst - Start int // index of start instruction - NumCap int // number of InstCapture insts in re -} - -// An InstOp is an instruction opcode. -type InstOp uint8 - -const ( - InstAlt InstOp = iota - InstAltMatch - InstCapture - InstEmptyWidth - InstMatch - InstFail - InstNop - InstRune -) - -// An EmptyOp specifies a kind or mixture of zero-width assertions. -type EmptyOp uint8 - -const ( - EmptyBeginLine EmptyOp = 1 << iota - EmptyEndLine - EmptyBeginText - EmptyEndText - EmptyWordBoundary - EmptyNoWordBoundary -) - -// An Inst is a single instruction in a regular expression program. -type Inst struct { - Op InstOp - Out uint32 // all but InstMatch, InstFail - Arg uint32 // InstAlt, InstAltMatch, InstCapture, InstEmptyWidth - Rune []int -} - -func (p *Prog) String() string { - var b bytes.Buffer - dumpProg(&b, p) - return b.String() -} - -// skipNop follows any no-op or capturing instructions -// and returns the resulting pc. -func (p *Prog) skipNop(pc uint32) *Inst { - i := &p.Inst[pc] - for i.Op == InstNop || i.Op == InstCapture { - pc = i.Out - i = &p.Inst[pc] - } - return i -} - -// Prefix returns a literal string that all matches for the -// regexp must start with. Complete is true if the prefix -// is the entire match. -func (p *Prog) Prefix() (prefix string, complete bool) { - i := p.skipNop(uint32(p.Start)) - - // Avoid allocation of buffer if prefix is empty. - if i.Op != InstRune || len(i.Rune) != 1 { - return "", i.Op == InstMatch - } - - // Have prefix; gather characters. - var buf bytes.Buffer - for i.Op == InstRune && len(i.Rune) == 1 { - buf.WriteRune(i.Rune[0]) - i = p.skipNop(i.Out) - } - return buf.String(), i.Op == InstMatch -} - -// StartCond returns the leading empty-width conditions that must -// be true in any match. It returns ^EmptyOp(0) if no matches are possible. -func (p *Prog) StartCond() EmptyOp { - var flag EmptyOp - pc := uint32(p.Start) - i := &p.Inst[pc] -Loop: - for { - switch i.Op { - case InstEmptyWidth: - flag |= EmptyOp(i.Arg) - case InstFail: - return ^EmptyOp(0) - case InstCapture, InstNop: - // skip - default: - break Loop - } - pc = i.Out - i = &p.Inst[pc] - } - return flag -} - -// MatchRune returns true if the instruction matches (and consumes) r. -// It should only be called when i.Op == InstRune. -func (i *Inst) MatchRune(r int) bool { - rune := i.Rune - - // Special case: single-rune slice is from literal string, not char class. - // TODO: Case folding. - if len(rune) == 1 { - return r == rune[0] - } - - // Peek at the first few pairs. - // Should handle ASCII well. - for j := 0; j < len(rune) && j <= 8; j += 2 { - if r < rune[j] { - return false - } - if r <= rune[j+1] { - return true - } - } - - // Otherwise binary search. - lo := 0 - hi := len(rune) / 2 - for lo < hi { - m := lo + (hi-lo)/2 - if c := rune[2*m]; c <= r { - if r <= rune[2*m+1] { - return true - } - lo = m + 1 - } else { - hi = m - } - } - return false -} - -// As per re2's Prog::IsWordChar. Determines whether rune is an ASCII word char. -// Since we act on runes, it would be easy to support Unicode here. -func wordRune(rune int) bool { - return rune == '_' || - ('A' <= rune && rune <= 'Z') || - ('a' <= rune && rune <= 'z') || - ('0' <= rune && rune <= '9') -} - -// MatchEmptyWidth returns true if the instruction matches -// an empty string between the runes before and after. -// It should only be called when i.Op == InstEmptyWidth. -func (i *Inst) MatchEmptyWidth(before int, after int) bool { - switch EmptyOp(i.Arg) { - case EmptyBeginLine: - return before == '\n' || before == -1 - case EmptyEndLine: - return after == '\n' || after == -1 - case EmptyBeginText: - return before == -1 - case EmptyEndText: - return after == -1 - case EmptyWordBoundary: - return wordRune(before) != wordRune(after) - case EmptyNoWordBoundary: - return wordRune(before) == wordRune(after) - } - panic("unknown empty width arg") -} - -func (i *Inst) String() string { - var b bytes.Buffer - dumpInst(&b, i) - return b.String() -} - -func bw(b *bytes.Buffer, args ...string) { - for _, s := range args { - b.WriteString(s) - } -} - -func dumpProg(b *bytes.Buffer, p *Prog) { - for j := range p.Inst { - i := &p.Inst[j] - pc := strconv.Itoa(j) - if len(pc) < 3 { - b.WriteString(" "[len(pc):]) - } - if j == p.Start { - pc += "*" - } - bw(b, pc, "\t") - dumpInst(b, i) - bw(b, "\n") - } -} - -func u32(i uint32) string { - return strconv.Uitoa64(uint64(i)) -} - -func dumpInst(b *bytes.Buffer, i *Inst) { - switch i.Op { - case InstAlt: - bw(b, "alt -> ", u32(i.Out), ", ", u32(i.Arg)) - case InstAltMatch: - bw(b, "altmatch -> ", u32(i.Out), ", ", u32(i.Arg)) - case InstCapture: - bw(b, "cap ", u32(i.Arg), " -> ", u32(i.Out)) - case InstEmptyWidth: - bw(b, "empty ", u32(i.Arg), " -> ", u32(i.Out)) - case InstMatch: - bw(b, "match") - case InstFail: - bw(b, "fail") - case InstNop: - bw(b, "nop -> ", u32(i.Out)) - case InstRune: - if i.Rune == nil { - // shouldn't happen - bw(b, "rune <nil>") - } - bw(b, "rune ", strconv.QuoteToASCII(string(i.Rune)), " -> ", u32(i.Out)) - } -} diff --git a/libgo/go/exp/regexp/syntax/prog_test.go b/libgo/go/exp/regexp/syntax/prog_test.go deleted file mode 100644 index 7be4281c27f..00000000000 --- a/libgo/go/exp/regexp/syntax/prog_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package syntax - -import ( - "testing" -) - -var compileTests = []struct { - Regexp string - Prog string -}{ - {"a", ` 0 fail - 1* rune "a" -> 2 - 2 match -`}, - {"[A-M][n-z]", ` 0 fail - 1* rune "AM" -> 2 - 2 rune "nz" -> 3 - 3 match -`}, - {"", ` 0 fail - 1* nop -> 2 - 2 match -`}, - {"a?", ` 0 fail - 1 rune "a" -> 3 - 2* alt -> 1, 3 - 3 match -`}, - {"a??", ` 0 fail - 1 rune "a" -> 3 - 2* alt -> 3, 1 - 3 match -`}, - {"a+", ` 0 fail - 1* rune "a" -> 2 - 2 alt -> 1, 3 - 3 match -`}, - {"a+?", ` 0 fail - 1* rune "a" -> 2 - 2 alt -> 3, 1 - 3 match -`}, - {"a*", ` 0 fail - 1 rune "a" -> 2 - 2* alt -> 1, 3 - 3 match -`}, - {"a*?", ` 0 fail - 1 rune "a" -> 2 - 2* alt -> 3, 1 - 3 match -`}, - {"a+b+", ` 0 fail - 1* rune "a" -> 2 - 2 alt -> 1, 3 - 3 rune "b" -> 4 - 4 alt -> 3, 5 - 5 match -`}, - {"(a+)(b+)", ` 0 fail - 1* cap 2 -> 2 - 2 rune "a" -> 3 - 3 alt -> 2, 4 - 4 cap 3 -> 5 - 5 cap 4 -> 6 - 6 rune "b" -> 7 - 7 alt -> 6, 8 - 8 cap 5 -> 9 - 9 match -`}, - {"a+|b+", ` 0 fail - 1 rune "a" -> 2 - 2 alt -> 1, 6 - 3 rune "b" -> 4 - 4 alt -> 3, 6 - 5* alt -> 1, 3 - 6 match -`}, -} - -func TestCompile(t *testing.T) { - for _, tt := range compileTests { - re, _ := Parse(tt.Regexp, Perl) - p, _ := Compile(re) - s := p.String() - if s != tt.Prog { - t.Errorf("compiled %#q:\n--- have\n%s---\n--- want\n%s---", tt.Regexp, s, tt.Prog) - } - } -} diff --git a/libgo/go/exp/regexp/syntax/regexp.go b/libgo/go/exp/regexp/syntax/regexp.go deleted file mode 100644 index 00a4addefc4..00000000000 --- a/libgo/go/exp/regexp/syntax/regexp.go +++ /dev/null @@ -1,284 +0,0 @@ -// 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 syntax parses regular expressions into syntax trees. -// WORK IN PROGRESS. -package syntax - -// Note to implementers: -// In this package, re is always a *Regexp and r is always a rune. - -import ( - "bytes" - "strconv" - "strings" - "unicode" -) - -// A Regexp is a node in a regular expression syntax tree. -type Regexp struct { - Op Op // operator - Flags Flags - Sub []*Regexp // subexpressions, if any - Sub0 [1]*Regexp // storage for short Sub - Rune []int // matched runes, for OpLiteral, OpCharClass - Rune0 [2]int // storage for short Rune - Min, Max int // min, max for OpRepeat - Cap int // capturing index, for OpCapture - Name string // capturing name, for OpCapture -} - -// An Op is a single regular expression operator. -type Op uint8 - -// Operators are listed in precedence order, tightest binding to weakest. -// Character class operators are listed simplest to most complex -// (OpLiteral, OpCharClass, OpAnyCharNotNL, OpAnyChar). - -const ( - OpNoMatch Op = 1 + iota // matches no strings - OpEmptyMatch // matches empty string - OpLiteral // matches Runes sequence - OpCharClass // matches Runes interpreted as range pair list - OpAnyCharNotNL // matches any character - OpAnyChar // matches any character - OpBeginLine // matches empty string at beginning of line - OpEndLine // matches empty string at end of line - OpBeginText // matches empty string at beginning of text - OpEndText // matches empty string at end of text - OpWordBoundary // matches word boundary `\b` - OpNoWordBoundary // matches word non-boundary `\B` - OpCapture // capturing subexpression with index Cap, optional name Name - OpStar // matches Sub[0] zero or more times - OpPlus // matches Sub[0] one or more times - OpQuest // matches Sub[0] zero or one times - OpRepeat // matches Sub[0] at least Min times, at most Max (Max == -1 is no limit) - OpConcat // matches concatenation of Subs - OpAlternate // matches alternation of Subs -) - -const opPseudo Op = 128 // where pseudo-ops start - -// Equal returns true if x and y have identical structure. -func (x *Regexp) Equal(y *Regexp) bool { - if x == nil || y == nil { - return x == y - } - if x.Op != y.Op { - return false - } - switch x.Op { - case OpEndText: - // The parse flags remember whether this is \z or \Z. - if x.Flags&WasDollar != y.Flags&WasDollar { - return false - } - - case OpLiteral, OpCharClass: - if len(x.Rune) != len(y.Rune) { - return false - } - for i, r := range x.Rune { - if r != y.Rune[i] { - return false - } - } - - case OpAlternate, OpConcat: - if len(x.Sub) != len(y.Sub) { - return false - } - for i, sub := range x.Sub { - if !sub.Equal(y.Sub[i]) { - return false - } - } - - case OpStar, OpPlus, OpQuest: - if x.Flags&NonGreedy != y.Flags&NonGreedy || !x.Sub[0].Equal(y.Sub[0]) { - return false - } - - case OpRepeat: - if x.Flags&NonGreedy != y.Flags&NonGreedy || x.Min != y.Min || x.Max != y.Max || !x.Sub[0].Equal(y.Sub[0]) { - return false - } - - case OpCapture: - if x.Cap != y.Cap || x.Name != y.Name || !x.Sub[0].Equal(y.Sub[0]) { - return false - } - } - return true -} - -// writeRegexp writes the Perl syntax for the regular expression re to b. -func writeRegexp(b *bytes.Buffer, re *Regexp) { - switch re.Op { - default: - b.WriteString("<invalid op" + strconv.Itoa(int(re.Op)) + ">") - case OpNoMatch: - b.WriteString(`[^\x00-\x{10FFFF}]`) - case OpEmptyMatch: - b.WriteString(`(?:)`) - case OpLiteral: - if re.Flags&FoldCase != 0 { - b.WriteString(`(?i:`) - } - for _, r := range re.Rune { - escape(b, r, false) - } - if re.Flags&FoldCase != 0 { - b.WriteString(`)`) - } - case OpCharClass: - if len(re.Rune)%2 != 0 { - b.WriteString(`[invalid char class]`) - break - } - b.WriteRune('[') - if len(re.Rune) == 0 { - b.WriteString(`^\x00-\x{10FFFF}`) - } else if re.Rune[0] == 0 && re.Rune[len(re.Rune)-1] == unicode.MaxRune { - // Contains 0 and MaxRune. Probably a negated class. - // Print the gaps. - b.WriteRune('^') - for i := 1; i < len(re.Rune)-1; i += 2 { - lo, hi := re.Rune[i]+1, re.Rune[i+1]-1 - escape(b, lo, lo == '-') - if lo != hi { - b.WriteRune('-') - escape(b, hi, hi == '-') - } - } - } else { - for i := 0; i < len(re.Rune); i += 2 { - lo, hi := re.Rune[i], re.Rune[i+1] - escape(b, lo, lo == '-') - if lo != hi { - b.WriteRune('-') - escape(b, hi, hi == '-') - } - } - } - b.WriteRune(']') - case OpAnyCharNotNL: - b.WriteString(`[^\n]`) - case OpAnyChar: - b.WriteRune('.') - case OpBeginLine: - b.WriteRune('^') - case OpEndLine: - b.WriteRune('$') - case OpBeginText: - b.WriteString(`\A`) - case OpEndText: - b.WriteString(`\z`) - case OpWordBoundary: - b.WriteString(`\b`) - case OpNoWordBoundary: - b.WriteString(`\B`) - case OpCapture: - if re.Name != "" { - b.WriteString(`(?P<`) - b.WriteString(re.Name) - b.WriteRune('>') - } else { - b.WriteRune('(') - } - if re.Sub[0].Op != OpEmptyMatch { - writeRegexp(b, re.Sub[0]) - } - b.WriteRune(')') - case OpStar, OpPlus, OpQuest, OpRepeat: - if sub := re.Sub[0]; sub.Op > OpCapture { - b.WriteString(`(?:`) - writeRegexp(b, sub) - b.WriteString(`)`) - } else { - writeRegexp(b, sub) - } - switch re.Op { - case OpStar: - b.WriteRune('*') - case OpPlus: - b.WriteRune('+') - case OpQuest: - b.WriteRune('?') - case OpRepeat: - b.WriteRune('{') - b.WriteString(strconv.Itoa(re.Min)) - if re.Max != re.Min { - b.WriteRune(',') - if re.Max >= 0 { - b.WriteString(strconv.Itoa(re.Max)) - } - } - b.WriteRune('}') - } - case OpConcat: - for _, sub := range re.Sub { - if sub.Op == OpAlternate { - b.WriteString(`(?:`) - writeRegexp(b, sub) - b.WriteString(`)`) - } else { - writeRegexp(b, sub) - } - } - case OpAlternate: - for i, sub := range re.Sub { - if i > 0 { - b.WriteRune('|') - } - writeRegexp(b, sub) - } - } -} - -func (re *Regexp) String() string { - var b bytes.Buffer - writeRegexp(&b, re) - return b.String() -} - -const meta = `\.+*?()|[]{}^$` - -func escape(b *bytes.Buffer, r int, force bool) { - if unicode.IsPrint(r) { - if strings.IndexRune(meta, r) >= 0 || force { - b.WriteRune('\\') - } - b.WriteRune(r) - return - } - - switch r { - case '\a': - b.WriteString(`\a`) - case '\f': - b.WriteString(`\f`) - case '\n': - b.WriteString(`\n`) - case '\r': - b.WriteString(`\r`) - case '\t': - b.WriteString(`\t`) - case '\v': - b.WriteString(`\v`) - default: - if r < 0x100 { - b.WriteString(`\x`) - s := strconv.Itob(r, 16) - if len(s) == 1 { - b.WriteRune('0') - } - b.WriteString(s) - break - } - b.WriteString(`\x{`) - b.WriteString(strconv.Itob(r, 16)) - b.WriteString(`}`) - } -} diff --git a/libgo/go/exp/regexp/syntax/simplify.go b/libgo/go/exp/regexp/syntax/simplify.go deleted file mode 100644 index 72390417bbe..00000000000 --- a/libgo/go/exp/regexp/syntax/simplify.go +++ /dev/null @@ -1,151 +0,0 @@ -// 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 syntax - -// Simplify returns a regexp equivalent to re but without counted repetitions -// and with various other simplifications, such as rewriting /(?:a+)+/ to /a+/. -// The resulting regexp will execute correctly but its string representation -// will not produce the same parse tree, because capturing parentheses -// may have been duplicated or removed. For example, the simplified form -// for /(x){1,2}/ is /(x)(x)?/ but both parentheses capture as $1. -// The returned regexp may share structure with or be the original. -func (re *Regexp) Simplify() *Regexp { - if re == nil { - return nil - } - switch re.Op { - case OpCapture, OpConcat, OpAlternate: - // Simplify children, building new Regexp if children change. - nre := re - for i, sub := range re.Sub { - nsub := sub.Simplify() - if nre == re && nsub != sub { - // Start a copy. - nre = new(Regexp) - *nre = *re - nre.Rune = nil - nre.Sub = append(nre.Sub0[:0], re.Sub[:i]...) - } - if nre != re { - nre.Sub = append(nre.Sub, nsub) - } - } - return nre - - case OpStar, OpPlus, OpQuest: - sub := re.Sub[0].Simplify() - return simplify1(re.Op, re.Flags, sub, re) - - case OpRepeat: - // Special special case: x{0} matches the empty string - // and doesn't even need to consider x. - if re.Min == 0 && re.Max == 0 { - return &Regexp{Op: OpEmptyMatch} - } - - // The fun begins. - sub := re.Sub[0].Simplify() - - // x{n,} means at least n matches of x. - if re.Max == -1 { - // Special case: x{0,} is x*. - if re.Min == 0 { - return simplify1(OpStar, re.Flags, sub, nil) - } - - // Special case: x{1,} is x+. - if re.Min == 1 { - return simplify1(OpPlus, re.Flags, sub, nil) - } - - // General case: x{4,} is xxxx+. - nre := &Regexp{Op: OpConcat} - nre.Sub = nre.Sub0[:0] - for i := 0; i < re.Min-1; i++ { - nre.Sub = append(nre.Sub, sub) - } - nre.Sub = append(nre.Sub, simplify1(OpPlus, re.Flags, sub, nil)) - return nre - } - - // Special case x{0} handled above. - - // Special case: x{1} is just x. - if re.Min == 1 && re.Max == 1 { - return sub - } - - // General case: x{n,m} means n copies of x and m copies of x? - // The machine will do less work if we nest the final m copies, - // so that x{2,5} = xx(x(x(x)?)?)? - - // Build leading prefix: xx. - var prefix *Regexp - if re.Min > 0 { - prefix = &Regexp{Op: OpConcat} - prefix.Sub = prefix.Sub0[:0] - for i := 0; i < re.Min; i++ { - prefix.Sub = append(prefix.Sub, sub) - } - } - - // Build and attach suffix: (x(x(x)?)?)? - if re.Max > re.Min { - suffix := simplify1(OpQuest, re.Flags, sub, nil) - for i := re.Min + 1; i < re.Max; i++ { - nre2 := &Regexp{Op: OpConcat} - nre2.Sub = append(nre2.Sub0[:0], sub, suffix) - suffix = simplify1(OpQuest, re.Flags, nre2, nil) - } - if prefix == nil { - return suffix - } - prefix.Sub = append(prefix.Sub, suffix) - } - if prefix != nil { - return prefix - } - - // Some degenerate case like min > max or min < max < 0. - // Handle as impossible match. - return &Regexp{Op: OpNoMatch} - } - - return re -} - -// simplify1 implements Simplify for the unary OpStar, -// OpPlus, and OpQuest operators. It returns the simple regexp -// equivalent to -// -// Regexp{Op: op, Flags: flags, Sub: {sub}} -// -// under the assumption that sub is already simple, and -// without first allocating that structure. If the regexp -// to be returned turns out to be equivalent to re, simplify1 -// returns re instead. -// -// simplify1 is factored out of Simplify because the implementation -// for other operators generates these unary expressions. -// Letting them call simplify1 makes sure the expressions they -// generate are simple. -func simplify1(op Op, flags Flags, sub, re *Regexp) *Regexp { - // Special case: repeat the empty string as much as - // you want, but it's still the empty string. - if sub.Op == OpEmptyMatch { - return sub - } - // The operators are idempotent if the flags match. - if op == sub.Op && flags&NonGreedy == sub.Flags&NonGreedy { - return sub - } - if re != nil && re.Op == op && re.Flags&NonGreedy == flags&NonGreedy && sub == re.Sub[0] { - return re - } - - re = &Regexp{Op: op, Flags: flags} - re.Sub = append(re.Sub0[:0], sub) - return re -} diff --git a/libgo/go/exp/regexp/syntax/simplify_test.go b/libgo/go/exp/regexp/syntax/simplify_test.go deleted file mode 100644 index c8cec21831a..00000000000 --- a/libgo/go/exp/regexp/syntax/simplify_test.go +++ /dev/null @@ -1,151 +0,0 @@ -// 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 syntax - -import "testing" - -var simplifyTests = []struct { - Regexp string - Simple string -}{ - // Already-simple constructs - {`a`, `a`}, - {`ab`, `ab`}, - {`a|b`, `[a-b]`}, - {`ab|cd`, `ab|cd`}, - {`(ab)*`, `(ab)*`}, - {`(ab)+`, `(ab)+`}, - {`(ab)?`, `(ab)?`}, - {`.`, `.`}, - {`^`, `^`}, - {`$`, `$`}, - {`[ac]`, `[ac]`}, - {`[^ac]`, `[^ac]`}, - - // Posix character classes - {`[[:alnum:]]`, `[0-9A-Za-z]`}, - {`[[:alpha:]]`, `[A-Za-z]`}, - {`[[:blank:]]`, `[\t ]`}, - {`[[:cntrl:]]`, `[\x00-\x1f\x7f]`}, - {`[[:digit:]]`, `[0-9]`}, - {`[[:graph:]]`, `[!-~]`}, - {`[[:lower:]]`, `[a-z]`}, - {`[[:print:]]`, `[ -~]`}, - {`[[:punct:]]`, "[!-/:-@\\[-`\\{-~]"}, - {`[[:space:]]`, `[\t-\r ]`}, - {`[[:upper:]]`, `[A-Z]`}, - {`[[:xdigit:]]`, `[0-9A-Fa-f]`}, - - // Perl character classes - {`\d`, `[0-9]`}, - {`\s`, `[\t-\n\f-\r ]`}, - {`\w`, `[0-9A-Z_a-z]`}, - {`\D`, `[^0-9]`}, - {`\S`, `[^\t-\n\f-\r ]`}, - {`\W`, `[^0-9A-Z_a-z]`}, - {`[\d]`, `[0-9]`}, - {`[\s]`, `[\t-\n\f-\r ]`}, - {`[\w]`, `[0-9A-Z_a-z]`}, - {`[\D]`, `[^0-9]`}, - {`[\S]`, `[^\t-\n\f-\r ]`}, - {`[\W]`, `[^0-9A-Z_a-z]`}, - - // Posix repetitions - {`a{1}`, `a`}, - {`a{2}`, `aa`}, - {`a{5}`, `aaaaa`}, - {`a{0,1}`, `a?`}, - // The next three are illegible because Simplify inserts (?:) - // parens instead of () parens to avoid creating extra - // captured subexpressions. The comments show a version with fewer parens. - {`(a){0,2}`, `(?:(a)(a)?)?`}, // (aa?)? - {`(a){0,4}`, `(?:(a)(?:(a)(?:(a)(a)?)?)?)?`}, // (a(a(aa?)?)?)? - {`(a){2,6}`, `(a)(a)(?:(a)(?:(a)(?:(a)(a)?)?)?)?`}, // aa(a(a(aa?)?)?)? - {`a{0,2}`, `(?:aa?)?`}, // (aa?)? - {`a{0,4}`, `(?:a(?:a(?:aa?)?)?)?`}, // (a(a(aa?)?)?)? - {`a{2,6}`, `aa(?:a(?:a(?:aa?)?)?)?`}, // aa(a(a(aa?)?)?)? - {`a{0,}`, `a*`}, - {`a{1,}`, `a+`}, - {`a{2,}`, `aa+`}, - {`a{5,}`, `aaaaa+`}, - - // Test that operators simplify their arguments. - {`(?:a{1,}){1,}`, `a+`}, - {`(a{1,}b{1,})`, `(a+b+)`}, - {`a{1,}|b{1,}`, `a+|b+`}, - {`(?:a{1,})*`, `(?:a+)*`}, - {`(?:a{1,})+`, `a+`}, - {`(?:a{1,})?`, `(?:a+)?`}, - {``, `(?:)`}, - {`a{0}`, `(?:)`}, - - // Character class simplification - {`[ab]`, `[a-b]`}, - {`[a-za-za-z]`, `[a-z]`}, - {`[A-Za-zA-Za-z]`, `[A-Za-z]`}, - {`[ABCDEFGH]`, `[A-H]`}, - {`[AB-CD-EF-GH]`, `[A-H]`}, - {`[W-ZP-XE-R]`, `[E-Z]`}, - {`[a-ee-gg-m]`, `[a-m]`}, - {`[a-ea-ha-m]`, `[a-m]`}, - {`[a-ma-ha-e]`, `[a-m]`}, - {`[a-zA-Z0-9 -~]`, `[ -~]`}, - - // Empty character classes - {`[^[:cntrl:][:^cntrl:]]`, `[^\x00-\x{10FFFF}]`}, - - // Full character classes - {`[[:cntrl:][:^cntrl:]]`, `.`}, - - // Unicode case folding. - {`(?i)A`, `(?i:A)`}, - {`(?i)a`, `(?i:a)`}, - {`(?i)[A]`, `(?i:A)`}, - {`(?i)[a]`, `(?i:A)`}, - {`(?i)K`, `(?i:K)`}, - {`(?i)k`, `(?i:k)`}, - {`(?i)\x{212a}`, "(?i:\u212A)"}, - {`(?i)[K]`, "[Kk\u212A]"}, - {`(?i)[k]`, "[Kk\u212A]"}, - {`(?i)[\x{212a}]`, "[Kk\u212A]"}, - {`(?i)[a-z]`, "[A-Za-z\u017F\u212A]"}, - {`(?i)[\x00-\x{FFFD}]`, "[\\x00-\uFFFD]"}, - {`(?i)[\x00-\x{10FFFF}]`, `.`}, - - // Empty string as a regular expression. - // The empty string must be preserved inside parens in order - // to make submatches work right, so these tests are less - // interesting than they might otherwise be. String inserts - // explicit (?:) in place of non-parenthesized empty strings, - // to make them easier to spot for other parsers. - {`(a|b|)`, `([a-b]|(?:))`}, - {`(|)`, `()`}, - {`a()`, `a()`}, - {`(()|())`, `(()|())`}, - {`(a|)`, `(a|(?:))`}, - {`ab()cd()`, `ab()cd()`}, - {`()`, `()`}, - {`()*`, `()*`}, - {`()+`, `()+`}, - {`()?`, `()?`}, - {`(){0}`, `(?:)`}, - {`(){1}`, `()`}, - {`(){1,}`, `()+`}, - {`(){0,2}`, `(?:()()?)?`}, -} - -func TestSimplify(t *testing.T) { - for _, tt := range simplifyTests { - re, err := Parse(tt.Regexp, MatchNL|Perl&^OneLine) - if err != nil { - t.Errorf("Parse(%#q) = error %v", tt.Regexp, err) - continue - } - s := re.Simplify().String() - if s != tt.Simple { - t.Errorf("Simplify(%#q) = %#q, want %#q", tt.Regexp, s, tt.Simple) - } - } -} diff --git a/libgo/go/exp/spdy/read.go b/libgo/go/exp/spdy/read.go new file mode 100644 index 00000000000..2b1fd3d0d42 --- /dev/null +++ b/libgo/go/exp/spdy/read.go @@ -0,0 +1,313 @@ +// 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 spdy + +import ( + "compress/zlib" + "encoding/binary" + "http" + "io" + "os" + "strings" +) + +func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) os.Error { + return f.readSynStreamFrame(h, frame) +} + +func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) os.Error { + return f.readSynReplyFrame(h, frame) +} + +func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) os.Error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { + return err + } + return nil +} + +func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) os.Error { + frame.CFHeader = h + var numSettings uint32 + if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { + return err + } + frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) + for i := uint32(0); i < numSettings; i++ { + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { + return err + } + frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24) + frame.FlagIdValues[i].Id &= 0xffffff + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil { + return err + } + } + return nil +} + +func (frame *NoopFrame) read(h ControlFrameHeader, f *Framer) os.Error { + frame.CFHeader = h + return nil +} + +func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) os.Error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil { + return err + } + return nil +} + +func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) os.Error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil { + return err + } + return nil +} + +func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) os.Error { + return f.readHeadersFrame(h, frame) +} + +func newControlFrame(frameType ControlFrameType) (controlFrame, os.Error) { + ctor, ok := cframeCtor[frameType] + if !ok { + return nil, &Error{Err: InvalidControlFrame} + } + return ctor(), nil +} + +var cframeCtor = map[ControlFrameType]func() controlFrame{ + TypeSynStream: func() controlFrame { return new(SynStreamFrame) }, + TypeSynReply: func() controlFrame { return new(SynReplyFrame) }, + TypeRstStream: func() controlFrame { return new(RstStreamFrame) }, + TypeSettings: func() controlFrame { return new(SettingsFrame) }, + TypeNoop: func() controlFrame { return new(NoopFrame) }, + TypePing: func() controlFrame { return new(PingFrame) }, + TypeGoAway: func() controlFrame { return new(GoAwayFrame) }, + TypeHeaders: func() controlFrame { return new(HeadersFrame) }, + // TODO(willchan): Add TypeWindowUpdate +} + +func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) os.Error { + if f.headerDecompressor != nil { + f.headerReader.N = payloadSize + return nil + } + f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} + decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(HeaderDictionary)) + if err != nil { + return err + } + f.headerDecompressor = decompressor + return nil +} + +// ReadFrame reads SPDY encoded data and returns a decompressed Frame. +func (f *Framer) ReadFrame() (Frame, os.Error) { + var firstWord uint32 + if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil { + return nil, err + } + if (firstWord & 0x80000000) != 0 { + frameType := ControlFrameType(firstWord & 0xffff) + version := uint16(0x7fff & (firstWord >> 16)) + return f.parseControlFrame(version, frameType) + } + return f.parseDataFrame(firstWord & 0x7fffffff) +} + +func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, os.Error) { + var length uint32 + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } + flags := ControlFlags((length & 0xff000000) >> 24) + length &= 0xffffff + header := ControlFrameHeader{version, frameType, flags, length} + cframe, err := newControlFrame(frameType) + if err != nil { + return nil, err + } + if err = cframe.read(header, f); err != nil { + return nil, err + } + return cframe, nil +} + +func parseHeaderValueBlock(r io.Reader, streamId uint32) (http.Header, os.Error) { + var numHeaders uint16 + if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { + return nil, err + } + var e os.Error + h := make(http.Header, int(numHeaders)) + for i := 0; i < int(numHeaders); i++ { + var length uint16 + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } + nameBytes := make([]byte, length) + if _, err := io.ReadFull(r, nameBytes); err != nil { + return nil, err + } + name := string(nameBytes) + if name != strings.ToLower(name) { + e = &Error{UnlowercasedHeaderName, streamId} + name = strings.ToLower(name) + } + if h[name] != nil { + e = &Error{DuplicateHeaders, streamId} + } + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } + value := make([]byte, length) + if _, err := io.ReadFull(r, value); err != nil { + return nil, err + } + valueList := strings.Split(string(value), "\x00") + for _, v := range valueList { + h.Add(name, v) + } + } + if e != nil { + return h, e + } + return h, nil +} + +func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) os.Error { + frame.CFHeader = h + var err os.Error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil { + return err + } + if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil { + return err + } + frame.Priority >>= 14 + + reader := f.r + if !f.headerCompressionDisabled { + f.uncorkHeaderDecompressor(int64(h.length - 10)) + reader = f.headerDecompressor + } + + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && ((err == os.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + // Remove this condition when we bump Version to 3. + if Version >= 3 { + for h := range frame.Headers { + if invalidReqHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + } + return nil +} + +func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) os.Error { + frame.CFHeader = h + var err os.Error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + var unused uint16 + if err = binary.Read(f.r, binary.BigEndian, &unused); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + f.uncorkHeaderDecompressor(int64(h.length - 6)) + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && ((err == os.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + // Remove this condition when we bump Version to 3. + if Version >= 3 { + for h := range frame.Headers { + if invalidRespHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + } + return nil +} + +func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) os.Error { + frame.CFHeader = h + var err os.Error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + var unused uint16 + if err = binary.Read(f.r, binary.BigEndian, &unused); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + f.uncorkHeaderDecompressor(int64(h.length - 6)) + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && ((err == os.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + + // Remove this condition when we bump Version to 3. + if Version >= 3 { + var invalidHeaders map[string]bool + if frame.StreamId%2 == 0 { + invalidHeaders = invalidReqHeaders + } else { + invalidHeaders = invalidRespHeaders + } + for h := range frame.Headers { + if invalidHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + } + return nil +} + +func (f *Framer) parseDataFrame(streamId uint32) (*DataFrame, os.Error) { + var length uint32 + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } + var frame DataFrame + frame.StreamId = streamId + frame.Flags = DataFlags(length >> 24) + length &= 0xffffff + frame.Data = make([]byte, length) + if _, err := io.ReadFull(f.r, frame.Data); err != nil { + return nil, err + } + return &frame, nil +} diff --git a/libgo/go/exp/spdy/spdy_test.go b/libgo/go/exp/spdy/spdy_test.go new file mode 100644 index 00000000000..cb91e028613 --- /dev/null +++ b/libgo/go/exp/spdy/spdy_test.go @@ -0,0 +1,497 @@ +// 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 spdy + +import ( + "bytes" + "http" + "io" + "reflect" + "testing" +) + +func TestHeaderParsing(t *testing.T) { + headers := http.Header{ + "Url": []string{"http://www.google.com/"}, + "Method": []string{"get"}, + "Version": []string{"http/1.1"}, + } + var headerValueBlockBuf bytes.Buffer + writeHeaderValueBlock(&headerValueBlockBuf, headers) + + const bogusStreamId = 1 + newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId) + if err != nil { + t.Fatal("parseHeaderValueBlock:", err) + } + + if !reflect.DeepEqual(headers, newHeaders) { + t.Fatal("got: ", newHeaders, "\nwant: ", headers) + } +} + +func TestCreateParseSynStreamFrame(t *testing.T) { + buffer := new(bytes.Buffer) + framer := &Framer{ + headerCompressionDisabled: true, + w: buffer, + headerBuf: new(bytes.Buffer), + r: buffer, + } + synStreamFrame := SynStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynStream, + }, + Headers: http.Header{ + "Url": []string{"http://www.google.com/"}, + "Method": []string{"get"}, + "Version": []string{"http/1.1"}, + }, + } + if err := framer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame without compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame without compression:", err) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } + + // Test again with compression + buffer.Reset() + framer, err = NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + if err := framer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame with compression:", err) + } + frame, err = framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame with compression:", err) + } + parsedSynStreamFrame, ok = frame.(*SynStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} + +func TestCreateParseSynReplyFrame(t *testing.T) { + buffer := new(bytes.Buffer) + framer := &Framer{ + headerCompressionDisabled: true, + w: buffer, + headerBuf: new(bytes.Buffer), + r: buffer, + } + synReplyFrame := SynReplyFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynReply, + }, + Headers: http.Header{ + "Url": []string{"http://www.google.com/"}, + "Method": []string{"get"}, + "Version": []string{"http/1.1"}, + }, + } + if err := framer.WriteFrame(&synReplyFrame); err != nil { + t.Fatal("WriteFrame without compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame without compression:", err) + } + parsedSynReplyFrame, ok := frame.(*SynReplyFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { + t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) + } + + // Test again with compression + buffer.Reset() + framer, err = NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + if err := framer.WriteFrame(&synReplyFrame); err != nil { + t.Fatal("WriteFrame with compression:", err) + } + frame, err = framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame with compression:", err) + } + parsedSynReplyFrame, ok = frame.(*SynReplyFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { + t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) + } +} + +func TestCreateParseRstStream(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + rstStreamFrame := RstStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeRstStream, + }, + StreamId: 1, + Status: InvalidStream, + } + if err := framer.WriteFrame(&rstStreamFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedRstStreamFrame, ok := frame.(*RstStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(rstStreamFrame, *parsedRstStreamFrame) { + t.Fatal("got: ", *parsedRstStreamFrame, "\nwant: ", rstStreamFrame) + } +} + +func TestCreateParseSettings(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + settingsFrame := SettingsFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSettings, + }, + FlagIdValues: []SettingsFlagIdValue{ + {FlagSettingsPersistValue, SettingsCurrentCwnd, 10}, + {FlagSettingsPersisted, SettingsUploadBandwidth, 1}, + }, + } + if err := framer.WriteFrame(&settingsFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedSettingsFrame, ok := frame.(*SettingsFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(settingsFrame, *parsedSettingsFrame) { + t.Fatal("got: ", *parsedSettingsFrame, "\nwant: ", settingsFrame) + } +} + +func TestCreateParseNoop(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + noopFrame := NoopFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeNoop, + }, + } + if err := framer.WriteFrame(&noopFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedNoopFrame, ok := frame.(*NoopFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(noopFrame, *parsedNoopFrame) { + t.Fatal("got: ", *parsedNoopFrame, "\nwant: ", noopFrame) + } +} + +func TestCreateParsePing(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + pingFrame := PingFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypePing, + }, + Id: 31337, + } + if err := framer.WriteFrame(&pingFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedPingFrame, ok := frame.(*PingFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(pingFrame, *parsedPingFrame) { + t.Fatal("got: ", *parsedPingFrame, "\nwant: ", pingFrame) + } +} + +func TestCreateParseGoAway(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + goAwayFrame := GoAwayFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeGoAway, + }, + LastGoodStreamId: 31337, + } + if err := framer.WriteFrame(&goAwayFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedGoAwayFrame, ok := frame.(*GoAwayFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(goAwayFrame, *parsedGoAwayFrame) { + t.Fatal("got: ", *parsedGoAwayFrame, "\nwant: ", goAwayFrame) + } +} + +func TestCreateParseHeadersFrame(t *testing.T) { + buffer := new(bytes.Buffer) + framer := &Framer{ + headerCompressionDisabled: true, + w: buffer, + headerBuf: new(bytes.Buffer), + r: buffer, + } + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + } + headersFrame.Headers = http.Header{ + "Url": []string{"http://www.google.com/"}, + "Method": []string{"get"}, + "Version": []string{"http/1.1"}, + } + if err := framer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame without compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame without compression:", err) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } + + // Test again with compression + buffer.Reset() + framer, err = NewFramer(buffer, buffer) + if err := framer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame with compression:", err) + } + frame, err = framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame with compression:", err) + } + parsedHeadersFrame, ok = frame.(*HeadersFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } +} + +func TestCreateParseDataFrame(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + dataFrame := DataFrame{ + StreamId: 1, + Data: []byte{'h', 'e', 'l', 'l', 'o'}, + } + if err := framer.WriteFrame(&dataFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedDataFrame, ok := frame.(*DataFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(dataFrame, *parsedDataFrame) { + t.Fatal("got: ", *parsedDataFrame, "\nwant: ", dataFrame) + } +} + +func TestCompressionContextAcrossFrames(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + Headers: http.Header{ + "Url": []string{"http://www.google.com/"}, + "Method": []string{"get"}, + "Version": []string{"http/1.1"}, + }, + } + if err := framer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame (HEADERS):", err) + } + synStreamFrame := SynStreamFrame{ControlFrameHeader{Version, TypeSynStream, 0, 0}, 0, 0, 0, nil} + synStreamFrame.Headers = http.Header{ + "Url": []string{"http://www.google.com/"}, + "Method": []string{"get"}, + "Version": []string{"http/1.1"}, + } + if err := framer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame (SYN_STREAM):", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (HEADERS):", err, buffer.Bytes()) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatalf("expected HeadersFrame; got %T %v", frame, frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } + frame, err = framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (SYN_STREAM):", err, buffer.Bytes()) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatalf("expected SynStreamFrame; got %T %v", frame, frame) + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} + +func TestMultipleSPDYFrames(t *testing.T) { + // Initialize the framers. + pr1, pw1 := io.Pipe() + pr2, pw2 := io.Pipe() + writer, err := NewFramer(pw1, pr2) + if err != nil { + t.Fatal("Failed to create writer:", err) + } + reader, err := NewFramer(pw2, pr1) + if err != nil { + t.Fatal("Failed to create reader:", err) + } + + // Set up the frames we're actually transferring. + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + Headers: http.Header{ + "Url": []string{"http://www.google.com/"}, + "Method": []string{"get"}, + "Version": []string{"http/1.1"}, + }, + } + synStreamFrame := SynStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynStream, + }, + Headers: http.Header{ + "Url": []string{"http://www.google.com/"}, + "Method": []string{"get"}, + "Version": []string{"http/1.1"}, + }, + } + + // Start the goroutines to write the frames. + go func() { + if err := writer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame (HEADERS): ", err) + } + if err := writer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame (SYN_STREAM): ", err) + } + }() + + // Read the frames and verify they look as expected. + frame, err := reader.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (HEADERS): ", err) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } + frame, err = reader.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (SYN_STREAM):", err) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type.") + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} diff --git a/libgo/go/exp/spdy/types.go b/libgo/go/exp/spdy/types.go new file mode 100644 index 00000000000..41cafb1741f --- /dev/null +++ b/libgo/go/exp/spdy/types.go @@ -0,0 +1,370 @@ +// 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 spdy + +import ( + "bytes" + "compress/zlib" + "http" + "io" + "os" +) + +// Data Frame Format +// +----------------------------------+ +// |0| Stream-ID (31bits) | +// +----------------------------------+ +// | flags (8) | Length (24 bits) | +// +----------------------------------+ +// | Data | +// +----------------------------------+ +// +// Control Frame Format +// +----------------------------------+ +// |1| Version(15bits) | Type(16bits) | +// +----------------------------------+ +// | flags (8) | Length (24 bits) | +// +----------------------------------+ +// | Data | +// +----------------------------------+ +// +// Control Frame: SYN_STREAM +// +----------------------------------+ +// |1|000000000000001|0000000000000001| +// +----------------------------------+ +// | flags (8) | Length (24 bits) | >= 12 +// +----------------------------------+ +// |X| Stream-ID(31bits) | +// +----------------------------------+ +// |X|Associated-To-Stream-ID (31bits)| +// +----------------------------------+ +// |Pri| unused | Length (16bits)| +// +----------------------------------+ +// +// Control Frame: SYN_REPLY +// +----------------------------------+ +// |1|000000000000001|0000000000000010| +// +----------------------------------+ +// | flags (8) | Length (24 bits) | >= 8 +// +----------------------------------+ +// |X| Stream-ID(31bits) | +// +----------------------------------+ +// | unused (16 bits)| Length (16bits)| +// +----------------------------------+ +// +// Control Frame: RST_STREAM +// +----------------------------------+ +// |1|000000000000001|0000000000000011| +// +----------------------------------+ +// | flags (8) | Length (24 bits) | >= 4 +// +----------------------------------+ +// |X| Stream-ID(31bits) | +// +----------------------------------+ +// | Status code (32 bits) | +// +----------------------------------+ +// +// Control Frame: SETTINGS +// +----------------------------------+ +// |1|000000000000001|0000000000000100| +// +----------------------------------+ +// | flags (8) | Length (24 bits) | +// +----------------------------------+ +// | # of entries (32) | +// +----------------------------------+ +// +// Control Frame: NOOP +// +----------------------------------+ +// |1|000000000000001|0000000000000101| +// +----------------------------------+ +// | flags (8) | Length (24 bits) | = 0 +// +----------------------------------+ +// +// Control Frame: PING +// +----------------------------------+ +// |1|000000000000001|0000000000000110| +// +----------------------------------+ +// | flags (8) | Length (24 bits) | = 4 +// +----------------------------------+ +// | Unique id (32 bits) | +// +----------------------------------+ +// +// Control Frame: GOAWAY +// +----------------------------------+ +// |1|000000000000001|0000000000000111| +// +----------------------------------+ +// | flags (8) | Length (24 bits) | = 4 +// +----------------------------------+ +// |X| Last-accepted-stream-id | +// +----------------------------------+ +// +// Control Frame: HEADERS +// +----------------------------------+ +// |1|000000000000001|0000000000001000| +// +----------------------------------+ +// | flags (8) | Length (24 bits) | >= 8 +// +----------------------------------+ +// |X| Stream-ID (31 bits) | +// +----------------------------------+ +// | unused (16 bits)| Length (16bits)| +// +----------------------------------+ +// +// Control Frame: WINDOW_UPDATE +// +----------------------------------+ +// |1|000000000000001|0000000000001001| +// +----------------------------------+ +// | flags (8) | Length (24 bits) | = 8 +// +----------------------------------+ +// |X| Stream-ID (31 bits) | +// +----------------------------------+ +// | Delta-Window-Size (32 bits) | +// +----------------------------------+ + +// Version is the protocol version number that this package implements. +const Version = 2 + +// ControlFrameType stores the type field in a control frame header. +type ControlFrameType uint16 + +// Control frame type constants +const ( + TypeSynStream ControlFrameType = 0x0001 + TypeSynReply = 0x0002 + TypeRstStream = 0x0003 + TypeSettings = 0x0004 + TypeNoop = 0x0005 + TypePing = 0x0006 + TypeGoAway = 0x0007 + TypeHeaders = 0x0008 + TypeWindowUpdate = 0x0009 +) + +// ControlFlags are the flags that can be set on a control frame. +type ControlFlags uint8 + +const ( + ControlFlagFin ControlFlags = 0x01 +) + +// DataFlags are the flags that can be set on a data frame. +type DataFlags uint8 + +const ( + DataFlagFin DataFlags = 0x01 + DataFlagCompressed = 0x02 +) + +// MaxDataLength is the maximum number of bytes that can be stored in one frame. +const MaxDataLength = 1<<24 - 1 + +// Frame is a single SPDY frame in its unpacked in-memory representation. Use +// Framer to read and write it. +type Frame interface { + write(f *Framer) os.Error +} + +// ControlFrameHeader contains all the fields in a control frame header, +// in its unpacked in-memory representation. +type ControlFrameHeader struct { + // Note, high bit is the "Control" bit. + version uint16 + frameType ControlFrameType + Flags ControlFlags + length uint32 +} + +type controlFrame interface { + Frame + read(h ControlFrameHeader, f *Framer) os.Error +} + +// SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM +// frame. +type SynStreamFrame struct { + CFHeader ControlFrameHeader + StreamId uint32 + AssociatedToStreamId uint32 + // Note, only 2 highest bits currently used + // Rest of Priority is unused. + Priority uint16 + Headers http.Header +} + +// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame. +type SynReplyFrame struct { + CFHeader ControlFrameHeader + StreamId uint32 + Headers http.Header +} + +// StatusCode represents the status that led to a RST_STREAM +type StatusCode uint32 + +const ( + ProtocolError StatusCode = 1 + InvalidStream = 2 + RefusedStream = 3 + UnsupportedVersion = 4 + Cancel = 5 + InternalError = 6 + FlowControlError = 7 +) + +// RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM +// frame. +type RstStreamFrame struct { + CFHeader ControlFrameHeader + StreamId uint32 + Status StatusCode +} + +// SettingsFlag represents a flag in a SETTINGS frame. +type SettingsFlag uint8 + +const ( + FlagSettingsPersistValue SettingsFlag = 0x1 + FlagSettingsPersisted = 0x2 +) + +// SettingsFlag represents the id of an id/value pair in a SETTINGS frame. +type SettingsId uint32 + +const ( + SettingsUploadBandwidth SettingsId = 1 + SettingsDownloadBandwidth = 2 + SettingsRoundTripTime = 3 + SettingsMaxConcurrentStreams = 4 + SettingsCurrentCwnd = 5 +) + +// SettingsFlagIdValue is the unpacked, in-memory representation of the +// combined flag/id/value for a setting in a SETTINGS frame. +type SettingsFlagIdValue struct { + Flag SettingsFlag + Id SettingsId + Value uint32 +} + +// SettingsFrame is the unpacked, in-memory representation of a SPDY +// SETTINGS frame. +type SettingsFrame struct { + CFHeader ControlFrameHeader + FlagIdValues []SettingsFlagIdValue +} + +// NoopFrame is the unpacked, in-memory representation of a NOOP frame. +type NoopFrame struct { + CFHeader ControlFrameHeader +} + +// PingFrame is the unpacked, in-memory representation of a PING frame. +type PingFrame struct { + CFHeader ControlFrameHeader + Id uint32 +} + +// GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame. +type GoAwayFrame struct { + CFHeader ControlFrameHeader + LastGoodStreamId uint32 +} + +// HeadersFrame is the unpacked, in-memory representation of a HEADERS frame. +type HeadersFrame struct { + CFHeader ControlFrameHeader + StreamId uint32 + Headers http.Header +} + +// DataFrame is the unpacked, in-memory representation of a DATA frame. +type DataFrame struct { + // Note, high bit is the "Control" bit. Should be 0 for data frames. + StreamId uint32 + Flags DataFlags + Data []byte +} + +// HeaderDictionary is the dictionary sent to the zlib compressor/decompressor. +// Even though the specification states there is no null byte at the end, Chrome sends it. +const HeaderDictionary = "optionsgetheadpostputdeletetrace" + + "acceptaccept-charsetaccept-encodingaccept-languageauthorizationexpectfromhost" + + "if-modified-sinceif-matchif-none-matchif-rangeif-unmodifiedsince" + + "max-forwardsproxy-authorizationrangerefererteuser-agent" + + "100101200201202203204205206300301302303304305306307400401402403404405406407408409410411412413414415416417500501502503504505" + + "accept-rangesageetaglocationproxy-authenticatepublicretry-after" + + "servervarywarningwww-authenticateallowcontent-basecontent-encodingcache-control" + + "connectiondatetrailertransfer-encodingupgradeviawarning" + + "content-languagecontent-lengthcontent-locationcontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookie" + + "MondayTuesdayWednesdayThursdayFridaySaturdaySunday" + + "JanFebMarAprMayJunJulAugSepOctNovDec" + + "chunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplication/xhtmltext/plainpublicmax-age" + + "charset=iso-8859-1utf-8gzipdeflateHTTP/1.1statusversionurl\x00" + +// A SPDY specific error. +type ErrorCode string + +const ( + UnlowercasedHeaderName ErrorCode = "header was not lowercased" + DuplicateHeaders ErrorCode = "multiple headers with same name" + WrongCompressedPayloadSize ErrorCode = "compressed payload size was incorrect" + UnknownFrameType ErrorCode = "unknown frame type" + InvalidControlFrame ErrorCode = "invalid control frame" + InvalidDataFrame ErrorCode = "invalid data frame" + InvalidHeaderPresent ErrorCode = "frame contained invalid header" +) + +// Error contains both the type of error and additional values. StreamId is 0 +// if Error is not associated with a stream. +type Error struct { + Err ErrorCode + StreamId uint32 +} + +func (e *Error) String() string { + return string(e.Err) +} + +var invalidReqHeaders = map[string]bool{ + "Connection": true, + "Keep-Alive": true, + "Proxy-Connection": true, + "Transfer-Encoding": true, +} + +var invalidRespHeaders = map[string]bool{ + "Connection": true, + "Keep-Alive": true, + "Transfer-Encoding": true, +} + +// Framer handles serializing/deserializing SPDY frames, including compressing/ +// decompressing payloads. +type Framer struct { + headerCompressionDisabled bool + w io.Writer + headerBuf *bytes.Buffer + headerCompressor *zlib.Writer + r io.Reader + headerReader io.LimitedReader + headerDecompressor io.ReadCloser +} + +// NewFramer allocates a new Framer for a given SPDY connection, repesented by +// a io.Writer and io.Reader. Note that Framer will read and write individual fields +// from/to the Reader and Writer, so the caller should pass in an appropriately +// buffered implementation to optimize performance. +func NewFramer(w io.Writer, r io.Reader) (*Framer, os.Error) { + compressBuf := new(bytes.Buffer) + compressor, err := zlib.NewWriterDict(compressBuf, zlib.BestCompression, []byte(HeaderDictionary)) + if err != nil { + return nil, err + } + framer := &Framer{ + w: w, + headerBuf: compressBuf, + headerCompressor: compressor, + r: r, + } + return framer, nil +} diff --git a/libgo/go/exp/spdy/write.go b/libgo/go/exp/spdy/write.go new file mode 100644 index 00000000000..7d40bbe9fe2 --- /dev/null +++ b/libgo/go/exp/spdy/write.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 spdy + +import ( + "encoding/binary" + "http" + "io" + "os" + "strings" +) + +func (frame *SynStreamFrame) write(f *Framer) os.Error { + return f.writeSynStreamFrame(frame) +} + +func (frame *SynReplyFrame) write(f *Framer) os.Error { + return f.writeSynReplyFrame(frame) +} + +func (frame *RstStreamFrame) write(f *Framer) (err os.Error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeRstStream + frame.CFHeader.length = 8 + + // Serialize frame to Writer + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { + return + } + return +} + +func (frame *SettingsFrame) write(f *Framer) (err os.Error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSettings + frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) + + // Serialize frame to Writer + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { + return + } + for _, flagIdValue := range frame.FlagIdValues { + flagId := (uint32(flagIdValue.Flag) << 24) | uint32(flagIdValue.Id) + if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil { + return + } + } + return +} + +func (frame *NoopFrame) write(f *Framer) os.Error { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeNoop + + // Serialize frame to Writer + return writeControlFrameHeader(f.w, frame.CFHeader) +} + +func (frame *PingFrame) write(f *Framer) (err os.Error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypePing + frame.CFHeader.length = 4 + + // Serialize frame to Writer + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil { + return + } + return +} + +func (frame *GoAwayFrame) write(f *Framer) (err os.Error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeGoAway + frame.CFHeader.length = 4 + + // Serialize frame to Writer + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil { + return + } + return nil +} + +func (frame *HeadersFrame) write(f *Framer) os.Error { + return f.writeHeadersFrame(frame) +} + +func (frame *DataFrame) write(f *Framer) os.Error { + return f.writeDataFrame(frame) +} + +// WriteFrame writes a frame. +func (f *Framer) WriteFrame(frame Frame) os.Error { + return frame.write(f) +} + +func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) os.Error { + if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil { + return err + } + flagsAndLength := (uint32(h.Flags) << 24) | h.length + if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil { + return err + } + return nil +} + +func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err os.Error) { + n = 0 + if err = binary.Write(w, binary.BigEndian, uint16(len(h))); err != nil { + return + } + n += 2 + for name, values := range h { + if err = binary.Write(w, binary.BigEndian, uint16(len(name))); err != nil { + return + } + n += 2 + name = strings.ToLower(name) + if _, err = io.WriteString(w, name); err != nil { + return + } + n += len(name) + v := strings.Join(values, "\x00") + if err = binary.Write(w, binary.BigEndian, uint16(len(v))); err != nil { + return + } + n += 2 + if _, err = io.WriteString(w, v); err != nil { + return + } + n += len(v) + } + return +} + +func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err os.Error) { + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynStream + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) + + // Serialize frame to Writer + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<14); err != nil { + return err + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return err + } + f.headerBuf.Reset() + return nil +} + +func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err os.Error) { + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynReply + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 6) + + // Serialize frame to Writer + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, uint16(0)); err != nil { + return + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return + } + f.headerBuf.Reset() + return +} + +func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err os.Error) { + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeHeaders + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 6) + + // Serialize frame to Writer + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, uint16(0)); err != nil { + return + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return + } + f.headerBuf.Reset() + return +} + +func (f *Framer) writeDataFrame(frame *DataFrame) (err os.Error) { + // Validate DataFrame + if frame.StreamId&0x80000000 != 0 || len(frame.Data) >= 0x0f000000 { + return &Error{InvalidDataFrame, frame.StreamId} + } + + // Serialize frame to Writer + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + flagsAndLength := (uint32(frame.Flags) << 24) | uint32(len(frame.Data)) + if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { + return + } + if _, err = f.w.Write(frame.Data); err != nil { + return + } + + return nil +} diff --git a/libgo/go/exp/sql/convert.go b/libgo/go/exp/sql/convert.go new file mode 100644 index 00000000000..a35e0be9cbe --- /dev/null +++ b/libgo/go/exp/sql/convert.go @@ -0,0 +1,106 @@ +// 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. + +// Type conversions for Scan. + +package sql + +import ( + "fmt" + "os" + "reflect" + "strconv" +) + +// convertAssign copies to dest the value in src, converting it if possible. +// An error is returned if the copy would result in loss of information. +// dest should be a pointer type. +func convertAssign(dest, src interface{}) os.Error { + // Common cases, without reflect. Fall through. + switch s := src.(type) { + case string: + switch d := dest.(type) { + case *string: + *d = s + return nil + } + case []byte: + switch d := dest.(type) { + case *string: + *d = string(s) + return nil + case *[]byte: + *d = s + return nil + } + } + + sv := reflect.ValueOf(src) + + switch d := dest.(type) { + case *string: + switch sv.Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + *d = fmt.Sprintf("%v", src) + return nil + } + } + + if scanner, ok := dest.(ScannerInto); ok { + return scanner.ScanInto(src) + } + + dpv := reflect.ValueOf(dest) + if dpv.Kind() != reflect.Ptr { + return os.NewError("destination not a pointer") + } + + dv := reflect.Indirect(dpv) + if dv.Kind() == sv.Kind() { + dv.Set(sv) + return nil + } + + switch dv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if s, ok := asString(src); ok { + i64, err := strconv.Atoi64(s) + if err != nil { + return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) + } + if dv.OverflowInt(i64) { + return fmt.Errorf("string %q overflows %s", s, dv.Kind()) + } + dv.SetInt(i64) + return nil + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if s, ok := asString(src); ok { + u64, err := strconv.Atoui64(s) + if err != nil { + return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) + } + if dv.OverflowUint(u64) { + return fmt.Errorf("string %q overflows %s", s, dv.Kind()) + } + dv.SetUint(u64) + return nil + } + } + + return fmt.Errorf("unsupported driver -> Scan pair: %T -> %T", src, dest) +} + +func asString(src interface{}) (s string, ok bool) { + switch v := src.(type) { + case string: + return v, true + case []byte: + return string(v), true + } + return "", false +} diff --git a/libgo/go/exp/sql/convert_test.go b/libgo/go/exp/sql/convert_test.go new file mode 100644 index 00000000000..88ba8e7a77a --- /dev/null +++ b/libgo/go/exp/sql/convert_test.go @@ -0,0 +1,108 @@ +// 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 sql + +import ( + "fmt" + "reflect" + "testing" +) + +type conversionTest struct { + s, d interface{} // source and destination + + // following are used if they're non-zero + wantint int64 + wantuint uint64 + wantstr string + wanterr string +} + +// Target variables for scanning into. +var ( + scanstr string + scanint int + scanint8 int8 + scanint16 int16 + scanint32 int32 + scanuint8 uint8 + scanuint16 uint16 +) + +var conversionTests = []conversionTest{ + // Exact conversions (destination pointer type matches source type) + {s: "foo", d: &scanstr, wantstr: "foo"}, + {s: 123, d: &scanint, wantint: 123}, + + // To strings + {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"}, + {s: 123, d: &scanstr, wantstr: "123"}, + {s: int8(123), d: &scanstr, wantstr: "123"}, + {s: int64(123), d: &scanstr, wantstr: "123"}, + {s: uint8(123), d: &scanstr, wantstr: "123"}, + {s: uint16(123), d: &scanstr, wantstr: "123"}, + {s: uint32(123), d: &scanstr, wantstr: "123"}, + {s: uint64(123), d: &scanstr, wantstr: "123"}, + {s: 1.5, d: &scanstr, wantstr: "1.5"}, + + // Strings to integers + {s: "255", d: &scanuint8, wantuint: 255}, + {s: "256", d: &scanuint8, wanterr: `string "256" overflows uint8`}, + {s: "256", d: &scanuint16, wantuint: 256}, + {s: "-1", d: &scanint, wantint: -1}, + {s: "foo", d: &scanint, wanterr: `converting string "foo" to a int: parsing "foo": Invalid argument`}, +} + +func intValue(intptr interface{}) int64 { + return reflect.Indirect(reflect.ValueOf(intptr)).Int() +} + +func uintValue(intptr interface{}) uint64 { + return reflect.Indirect(reflect.ValueOf(intptr)).Uint() +} + +func TestConversions(t *testing.T) { + for n, ct := range conversionTests { + err := convertAssign(ct.d, ct.s) + errstr := "" + if err != nil { + errstr = err.String() + } + errf := func(format string, args ...interface{}) { + base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d) + t.Errorf(base+format, args...) + } + if errstr != ct.wanterr { + errf("got error %q, want error %q", errstr, ct.wanterr) + } + if ct.wantstr != "" && ct.wantstr != scanstr { + errf("want string %q, got %q", ct.wantstr, scanstr) + } + if ct.wantint != 0 && ct.wantint != intValue(ct.d) { + errf("want int %d, got %d", ct.wantint, intValue(ct.d)) + } + if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) { + errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d)) + } + } +} + +func TestNullableString(t *testing.T) { + var ns NullableString + convertAssign(&ns, []byte("foo")) + if !ns.Valid { + t.Errorf("expecting not null") + } + if ns.String != "foo" { + t.Errorf("expecting foo; got %q", ns.String) + } + convertAssign(&ns, nil) + if ns.Valid { + t.Errorf("expecting null on nil") + } + if ns.String != "" { + t.Errorf("expecting blank on nil; got %q", ns.String) + } +} diff --git a/libgo/go/exp/sql/driver/driver.go b/libgo/go/exp/sql/driver/driver.go new file mode 100644 index 00000000000..7508b19fa19 --- /dev/null +++ b/libgo/go/exp/sql/driver/driver.go @@ -0,0 +1,169 @@ +// 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 driver defines interfaces to be implemented by database +// drivers as used by package sql. +// +// Code simply using databases should use package sql. +// +// Drivers only need to be aware of a subset of Go's types. The db package +// will convert all types into one of the following: +// +// int64 +// float64 +// bool +// nil +// []byte +// string [*] everywhere except from Rows.Next. +// +package driver + +import ( + "os" +) + +// Driver is the interface that must be implemented by a database +// driver. +type Driver interface { + // Open returns a new or cached connection to the database. + // The name is a string in a driver-specific format. + // + // The returned connection is only used by one goroutine at a + // time. + Open(name string) (Conn, os.Error) +} + +// Execer is an optional interface that may be implemented by a Driver +// or a Conn. +// +// If a Driver does not implement Execer, the sql package's DB.Exec +// method first obtains a free connection from its free pool or from +// the driver's Open method. Execer should only be implemented by +// drivers that can provide a more efficient implementation. +// +// If a Conn does not implement Execer, the db package's DB.Exec will +// first prepare a query, execute the statement, and then close the +// statement. +// +// All arguments are of a subset type as defined in the package docs. +type Execer interface { + Exec(query string, args []interface{}) (Result, os.Error) +} + +// Conn is a connection to a database. It is not used concurrently +// by multiple goroutines. +// +// Conn is assumed to be stateful. +type Conn interface { + // Prepare returns a prepared statement, bound to this connection. + Prepare(query string) (Stmt, os.Error) + + // Close invalidates and potentially stops any current + // prepared statements and transactions, marking this + // connection as no longer in use. The driver may cache or + // close its underlying connection to its database. + Close() os.Error + + // Begin starts and returns a new transaction. + Begin() (Tx, os.Error) +} + +// Result is the result of a query execution. +type Result interface { + // LastInsertId returns the database's auto-generated ID + // after, for example, an INSERT into a table with primary + // key. + LastInsertId() (int64, os.Error) + + // RowsAffected returns the number of rows affected by the + // query. + RowsAffected() (int64, os.Error) +} + +// Stmt is a prepared statement. It is bound to a Conn and not +// used by multiple goroutines concurrently. +type Stmt interface { + // Close closes the statement. + Close() os.Error + + // NumInput returns the number of placeholder parameters. + NumInput() int + + // Exec executes a query that doesn't return rows, such + // as an INSERT or UPDATE. The args are all of a subset + // type as defined above. + Exec(args []interface{}) (Result, os.Error) + + // Exec executes a query that may return rows, such as a + // SELECT. The args of all of a subset type as defined above. + Query(args []interface{}) (Rows, os.Error) +} + +// ColumnConverter may be optionally implemented by Stmt if the +// the statement is aware of its own columns' types and can +// convert from any type to a driver subset type. +type ColumnConverter interface { + // ColumnConverter returns a ValueConverter for the provided + // column index. If the type of a specific column isn't known + // or shouldn't be handled specially, DefaultValueConverter + // can be returned. + ColumnConverter(idx int) ValueConverter +} + +// Rows is an iterator over an executed query's results. +type Rows interface { + // Columns returns the names of the columns. The number of + // columns of the result is inferred from the length of the + // slice. If a particular column name isn't known, an empty + // string should be returned for that entry. + Columns() []string + + // Close closes the rows iterator. + Close() os.Error + + // Next is called to populate the next row of data into + // the provided slice. The provided slice will be the same + // size as the Columns() are wide. + // + // The dest slice may be populated with only with values + // of subset types defined above, but excluding string. + // All string values must be converted to []byte. + Next(dest []interface{}) os.Error +} + +// Tx is a transaction. +type Tx interface { + Commit() os.Error + Rollback() os.Error +} + +// RowsAffected implements Result for an INSERT or UPDATE operation +// which mutates a number of rows. +type RowsAffected int64 + +var _ Result = RowsAffected(0) + +func (RowsAffected) LastInsertId() (int64, os.Error) { + return 0, os.NewError("no LastInsertId available") +} + +func (v RowsAffected) RowsAffected() (int64, os.Error) { + return int64(v), nil +} + +// DDLSuccess is a pre-defined Result for drivers to return when a DDL +// command succeeds. +var DDLSuccess ddlSuccess + +type ddlSuccess struct{} + +var _ Result = ddlSuccess{} + +func (ddlSuccess) LastInsertId() (int64, os.Error) { + return 0, os.NewError("no LastInsertId available after DDL statement") +} + +func (ddlSuccess) RowsAffected() (int64, os.Error) { + return 0, os.NewError("no RowsAffected available after DDL statement") +} diff --git a/libgo/go/exp/sql/driver/types.go b/libgo/go/exp/sql/driver/types.go new file mode 100644 index 00000000000..5521d5389c3 --- /dev/null +++ b/libgo/go/exp/sql/driver/types.go @@ -0,0 +1,161 @@ +// 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 driver + +import ( + "fmt" + "os" + "reflect" + "strconv" +) + +// ValueConverter is the interface providing the ConvertValue method. +type ValueConverter interface { + // ConvertValue converts a value to a restricted subset type. + ConvertValue(v interface{}) (interface{}, os.Error) +} + +// Bool is a ValueConverter that converts input values to bools. +// +// The conversion rules are: +// - .... TODO(bradfitz): TBD +var Bool boolType + +type boolType struct{} + +var _ ValueConverter = boolType{} + +func (boolType) ConvertValue(v interface{}) (interface{}, os.Error) { + return nil, fmt.Errorf("TODO(bradfitz): bool conversions") +} + +// Int32 is a ValueConverter that converts input values to int64, +// respecting the limits of an int32 value. +var Int32 int32Type + +type int32Type struct{} + +var _ ValueConverter = int32Type{} + +func (int32Type) ConvertValue(v interface{}) (interface{}, os.Error) { + rv := reflect.ValueOf(v) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i64 := rv.Int() + if i64 > (1<<31)-1 || i64 < -(1<<31) { + return nil, fmt.Errorf("sql/driver: value %d overflows int32", v) + } + return i64, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + u64 := rv.Uint() + if u64 > (1<<31)-1 { + return nil, fmt.Errorf("sql/driver: value %d overflows int32", v) + } + return int64(u64), nil + case reflect.String: + i, err := strconv.Atoi(rv.String()) + if err != nil { + return nil, fmt.Errorf("sql/driver: value %q can't be converted to int32", v) + } + return int64(i), nil + } + return nil, fmt.Errorf("sql/driver: unsupported value %v (type %T) converting to int32", v, v) +} + +// String is a ValueConverter that converts its input to a string. +// If the value is already a string or []byte, it's unchanged. +// If the value is of another type, conversion to string is done +// with fmt.Sprintf("%v", v). +var String stringType + +type stringType struct{} + +func (stringType) ConvertValue(v interface{}) (interface{}, os.Error) { + switch v.(type) { + case string, []byte: + return v, nil + } + return fmt.Sprintf("%v", v), nil +} + +// IsParameterSubsetType reports whether v is of a valid type for a +// parameter. These types are: +// +// int64 +// float64 +// bool +// nil +// []byte +// string +// +// This is the ame list as IsScanSubsetType, with the addition of +// string. +func IsParameterSubsetType(v interface{}) bool { + if IsScanSubsetType(v) { + return true + } + if _, ok := v.(string); ok { + return true + } + return false +} + +// IsScanSubsetType reports whether v is of a valid type for a +// value populated by Rows.Next. These types are: +// +// int64 +// float64 +// bool +// nil +// []byte +// +// This is the same list as IsParameterSubsetType, without string. +func IsScanSubsetType(v interface{}) bool { + if v == nil { + return true + } + switch v.(type) { + case int64, float64, []byte, bool: + return true + } + return false +} + +// DefaultParameterConverter is the default implementation of +// ValueConverter that's used when a Stmt doesn't implement +// ColumnConverter. +// +// DefaultParameterConverter returns the given value directly if +// IsSubsetType(value). Otherwise integer type are converted to +// int64, floats to float64, and strings to []byte. Other types are +// an error. +var DefaultParameterConverter defaultConverter + +type defaultConverter struct{} + +var _ ValueConverter = defaultConverter{} + +func (defaultConverter) ConvertValue(v interface{}) (interface{}, os.Error) { + if IsParameterSubsetType(v) { + return v, nil + } + + rv := reflect.ValueOf(v) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int(), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: + return int64(rv.Uint()), nil + case reflect.Uint64: + u64 := rv.Uint() + if u64 >= 1<<63 { + return nil, fmt.Errorf("uint64 values with high bit set are not supported") + } + return int64(u64), nil + case reflect.Float32, reflect.Float64: + return rv.Float(), nil + } + return nil, fmt.Errorf("unsupported type %s", rv.Kind()) +} diff --git a/libgo/go/exp/sql/fakedb_test.go b/libgo/go/exp/sql/fakedb_test.go new file mode 100644 index 00000000000..c906185e584 --- /dev/null +++ b/libgo/go/exp/sql/fakedb_test.go @@ -0,0 +1,497 @@ +// 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 sql + +import ( + "fmt" + "log" + "os" + "strconv" + "strings" + "sync" + + "exp/sql/driver" +) + +var _ = log.Printf + +// fakeDriver is a fake database that implements Go's driver.Driver +// interface, just for testing. +// +// It speaks a query language that's semantically similar to but +// syntantically different and simpler than SQL. The syntax is as +// follows: +// +// WIPE +// CREATE|<tablename>|<col>=<type>,<col>=<type>,... +// where types are: "string", [u]int{8,16,32,64}, "bool" +// INSERT|<tablename>|col=val,col2=val2,col3=? +// SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=? +// +// When opening a a fakeDriver's database, it starts empty with no +// tables. All tables and data are stored in memory only. +type fakeDriver struct { + mu sync.Mutex + openCount int + dbs map[string]*fakeDB +} + +type fakeDB struct { + name string + + mu sync.Mutex + free []*fakeConn + tables map[string]*table +} + +type table struct { + mu sync.Mutex + colname []string + coltype []string + rows []*row +} + +func (t *table) columnIndex(name string) int { + for n, nname := range t.colname { + if name == nname { + return n + } + } + return -1 +} + +type row struct { + cols []interface{} // must be same size as its table colname + coltype +} + +func (r *row) clone() *row { + nrow := &row{cols: make([]interface{}, len(r.cols))} + copy(nrow.cols, r.cols) + return nrow +} + +type fakeConn struct { + db *fakeDB // where to return ourselves to + + currTx *fakeTx +} + +type fakeTx struct { + c *fakeConn +} + +type fakeStmt struct { + c *fakeConn + q string // just for debugging + + cmd string + table string + + colName []string // used by CREATE, INSERT, SELECT (selected columns) + colType []string // used by CREATE + colValue []interface{} // used by INSERT (mix of strings and "?" for bound params) + placeholders int // used by INSERT/SELECT: number of ? params + + whereCol []string // used by SELECT (all placeholders) + + placeholderConverter []driver.ValueConverter // used by INSERT +} + +var fdriver driver.Driver = &fakeDriver{} + +func init() { + Register("test", fdriver) +} + +// Supports dsn forms: +// <dbname> +// <dbname>;wipe +func (d *fakeDriver) Open(dsn string) (driver.Conn, os.Error) { + d.mu.Lock() + defer d.mu.Unlock() + d.openCount++ + if d.dbs == nil { + d.dbs = make(map[string]*fakeDB) + } + parts := strings.Split(dsn, ";") + if len(parts) < 1 { + return nil, os.NewError("fakedb: no database name") + } + name := parts[0] + db, ok := d.dbs[name] + if !ok { + db = &fakeDB{name: name} + d.dbs[name] = db + } + return &fakeConn{db: db}, nil +} + +func (db *fakeDB) wipe() { + db.mu.Lock() + defer db.mu.Unlock() + db.tables = nil +} + +func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) os.Error { + db.mu.Lock() + defer db.mu.Unlock() + if db.tables == nil { + db.tables = make(map[string]*table) + } + if _, exist := db.tables[name]; exist { + return fmt.Errorf("table %q already exists", name) + } + if len(columnNames) != len(columnTypes) { + return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d", + name, len(columnNames), len(columnTypes)) + } + db.tables[name] = &table{colname: columnNames, coltype: columnTypes} + return nil +} + +// must be called with db.mu lock held +func (db *fakeDB) table(table string) (*table, bool) { + if db.tables == nil { + return nil, false + } + t, ok := db.tables[table] + return t, ok +} + +func (db *fakeDB) columnType(table, column string) (typ string, ok bool) { + db.mu.Lock() + defer db.mu.Unlock() + t, ok := db.table(table) + if !ok { + return + } + for n, cname := range t.colname { + if cname == column { + return t.coltype[n], true + } + } + return "", false +} + +func (c *fakeConn) Begin() (driver.Tx, os.Error) { + if c.currTx != nil { + return nil, os.NewError("already in a transaction") + } + c.currTx = &fakeTx{c: c} + return c.currTx, nil +} + +func (c *fakeConn) Close() os.Error { + if c.currTx != nil { + return os.NewError("can't close; in a Transaction") + } + if c.db == nil { + return os.NewError("can't close; already closed") + } + c.db = nil + return nil +} + +func errf(msg string, args ...interface{}) os.Error { + return os.NewError("fakedb: " + fmt.Sprintf(msg, args...)) +} + +// parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=? +// (note that where where columns must always contain ? marks, +// just a limitation for fakedb) +func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, os.Error) { + if len(parts) != 3 { + return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts)) + } + stmt.table = parts[0] + stmt.colName = strings.Split(parts[1], ",") + for n, colspec := range strings.Split(parts[2], ",") { + nameVal := strings.Split(colspec, "=") + if len(nameVal) != 2 { + return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) + } + column, value := nameVal[0], nameVal[1] + _, ok := c.db.columnType(stmt.table, column) + if !ok { + return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column) + } + if value != "?" { + return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark", + stmt.table, column) + } + stmt.whereCol = append(stmt.whereCol, column) + stmt.placeholders++ + } + return stmt, nil +} + +// parts are table|col=type,col2=type2 +func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, os.Error) { + if len(parts) != 2 { + return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts)) + } + stmt.table = parts[0] + for n, colspec := range strings.Split(parts[1], ",") { + nameType := strings.Split(colspec, "=") + if len(nameType) != 2 { + return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) + } + stmt.colName = append(stmt.colName, nameType[0]) + stmt.colType = append(stmt.colType, nameType[1]) + } + return stmt, nil +} + +// parts are table|col=?,col2=val +func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, os.Error) { + if len(parts) != 2 { + return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts)) + } + stmt.table = parts[0] + for n, colspec := range strings.Split(parts[1], ",") { + nameVal := strings.Split(colspec, "=") + if len(nameVal) != 2 { + return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) + } + column, value := nameVal[0], nameVal[1] + ctype, ok := c.db.columnType(stmt.table, column) + if !ok { + return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column) + } + stmt.colName = append(stmt.colName, column) + + if value != "?" { + var subsetVal interface{} + // Convert to driver subset type + switch ctype { + case "string": + subsetVal = []byte(value) + case "int32": + i, err := strconv.Atoi(value) + if err != nil { + return nil, errf("invalid conversion to int32 from %q", value) + } + subsetVal = int64(i) // int64 is a subset type, but not int32 + default: + return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype) + } + stmt.colValue = append(stmt.colValue, subsetVal) + } else { + stmt.placeholders++ + stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype)) + stmt.colValue = append(stmt.colValue, "?") + } + } + return stmt, nil +} + +func (c *fakeConn) Prepare(query string) (driver.Stmt, os.Error) { + if c.db == nil { + panic("nil c.db; conn = " + fmt.Sprintf("%#v", c)) + } + parts := strings.Split(query, "|") + if len(parts) < 1 { + return nil, errf("empty query") + } + cmd := parts[0] + parts = parts[1:] + stmt := &fakeStmt{q: query, c: c, cmd: cmd} + switch cmd { + case "WIPE": + // Nothing + case "SELECT": + return c.prepareSelect(stmt, parts) + case "CREATE": + return c.prepareCreate(stmt, parts) + case "INSERT": + return c.prepareInsert(stmt, parts) + default: + return nil, errf("unsupported command type %q", cmd) + } + return stmt, nil +} + +func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter { + return s.placeholderConverter[idx] +} + +func (s *fakeStmt) Close() os.Error { + return nil +} + +func (s *fakeStmt) Exec(args []interface{}) (driver.Result, os.Error) { + db := s.c.db + switch s.cmd { + case "WIPE": + db.wipe() + return driver.DDLSuccess, nil + case "CREATE": + if err := db.createTable(s.table, s.colName, s.colType); err != nil { + return nil, err + } + return driver.DDLSuccess, nil + case "INSERT": + return s.execInsert(args) + } + fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s) + return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd) +} + +func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, os.Error) { + db := s.c.db + if len(args) != s.placeholders { + panic("error in pkg db; should only get here if size is correct") + } + db.mu.Lock() + t, ok := db.table(s.table) + db.mu.Unlock() + if !ok { + return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table) + } + + t.mu.Lock() + defer t.mu.Unlock() + + cols := make([]interface{}, len(t.colname)) + argPos := 0 + for n, colname := range s.colName { + colidx := t.columnIndex(colname) + if colidx == -1 { + return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname) + } + var val interface{} + if strvalue, ok := s.colValue[n].(string); ok && strvalue == "?" { + val = args[argPos] + argPos++ + } else { + val = s.colValue[n] + } + cols[colidx] = val + } + + t.rows = append(t.rows, &row{cols: cols}) + return driver.RowsAffected(1), nil +} + +func (s *fakeStmt) Query(args []interface{}) (driver.Rows, os.Error) { + db := s.c.db + if len(args) != s.placeholders { + panic("error in pkg db; should only get here if size is correct") + } + + db.mu.Lock() + t, ok := db.table(s.table) + db.mu.Unlock() + if !ok { + return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table) + } + t.mu.Lock() + defer t.mu.Unlock() + + colIdx := make(map[string]int) // select column name -> column index in table + for _, name := range s.colName { + idx := t.columnIndex(name) + if idx == -1 { + return nil, fmt.Errorf("fakedb: unknown column name %q", name) + } + colIdx[name] = idx + } + + mrows := []*row{} +rows: + for _, trow := range t.rows { + // Process the where clause, skipping non-match rows. This is lazy + // and just uses fmt.Sprintf("%v") to test equality. Good enough + // for test code. + for widx, wcol := range s.whereCol { + idx := t.columnIndex(wcol) + if idx == -1 { + return nil, fmt.Errorf("db: invalid where clause column %q", wcol) + } + tcol := trow.cols[idx] + if bs, ok := tcol.([]byte); ok { + // lazy hack to avoid sprintf %v on a []byte + tcol = string(bs) + } + if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", args[widx]) { + continue rows + } + } + mrow := &row{cols: make([]interface{}, len(s.colName))} + for seli, name := range s.colName { + mrow.cols[seli] = trow.cols[colIdx[name]] + } + mrows = append(mrows, mrow) + } + + cursor := &rowsCursor{ + pos: -1, + rows: mrows, + cols: s.colName, + } + return cursor, nil +} + +func (s *fakeStmt) NumInput() int { + return s.placeholders +} + +func (tx *fakeTx) Commit() os.Error { + tx.c.currTx = nil + return nil +} + +func (tx *fakeTx) Rollback() os.Error { + tx.c.currTx = nil + return nil +} + +type rowsCursor struct { + cols []string + pos int + rows []*row + closed bool +} + +func (rc *rowsCursor) Close() os.Error { + rc.closed = true + return nil +} + +func (rc *rowsCursor) Columns() []string { + return rc.cols +} + +func (rc *rowsCursor) Next(dest []interface{}) os.Error { + if rc.closed { + return os.NewError("fakedb: cursor is closed") + } + rc.pos++ + if rc.pos >= len(rc.rows) { + return os.EOF // per interface spec + } + for i, v := range rc.rows[rc.pos].cols { + // TODO(bradfitz): convert to subset types? naah, I + // think the subset types should only be input to + // driver, but the db package should be able to handle + // a wider range of types coming out of drivers. all + // for ease of drivers, and to prevent drivers from + // messing up conversions or doing them differently. + dest[i] = v + } + return nil +} + +func converterForType(typ string) driver.ValueConverter { + switch typ { + case "bool": + return driver.Bool + case "int32": + return driver.Int32 + case "string": + return driver.String + } + panic("invalid fakedb column type of " + typ) +} diff --git a/libgo/go/exp/sql/sql.go b/libgo/go/exp/sql/sql.go new file mode 100644 index 00000000000..7f0e0b28425 --- /dev/null +++ b/libgo/go/exp/sql/sql.go @@ -0,0 +1,578 @@ +// 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 sql provides a generic interface around SQL (or SQL-like) +// databases. +package sql + +import ( + "fmt" + "os" + "runtime" + "sync" + + "exp/sql/driver" +) + +var drivers = make(map[string]driver.Driver) + +// Register makes a database driver available by the provided name. +// If Register is called twice with the same name or if driver is nil, +// it panics. +func Register(name string, driver driver.Driver) { + if driver == nil { + panic("db: Register driver is nil") + } + if _, dup := drivers[name]; dup { + panic("db: Register called twice for driver " + name) + } + drivers[name] = driver +} + +// NullableString represents a string that may be null. +// NullableString implements the ScannerInto interface so +// it can be used as a scan destination: +// +// var s NullableString +// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s) +// ... +// if s.Valid { +// // use s.String +// } else { +// // NULL value +// } +// +// TODO(bradfitz): add other types. +type NullableString struct { + String string + Valid bool // Valid is true if String is not NULL +} + +// ScanInto implements the ScannerInto interface. +func (ms *NullableString) ScanInto(value interface{}) os.Error { + if value == nil { + ms.String, ms.Valid = "", false + return nil + } + ms.Valid = true + return convertAssign(&ms.String, value) +} + +// ScannerInto is an interface used by Scan. +type ScannerInto interface { + // ScanInto assigns a value from a database driver. + // + // The value will be of one of the following restricted + // set of types: + // + // int64 + // float64 + // bool + // []byte + // nil - for NULL values + // + // An error should be returned if the value can not be stored + // without loss of information. + ScanInto(value interface{}) os.Error +} + +// ErrNoRows is returned by Scan when QueryRow doesn't return a +// row. In such a case, QueryRow returns a placeholder *Row value that +// defers this error until a Scan. +var ErrNoRows = os.NewError("db: no rows in result set") + +// DB is a database handle. It's safe for concurrent use by multiple +// goroutines. +type DB struct { + driver driver.Driver + dsn string + + mu sync.Mutex + freeConn []driver.Conn +} + +// Open opens a database specified by its database driver name and a +// driver-specific data source name, usually consisting of at least a +// database name and connection information. +// +// Most users will open a database via a driver-specific connection +// helper function that returns a *DB. +func Open(driverName, dataSourceName string) (*DB, os.Error) { + driver, ok := drivers[driverName] + if !ok { + return nil, fmt.Errorf("db: unknown driver %q (forgotten import?)", driverName) + } + return &DB{driver: driver, dsn: dataSourceName}, nil +} + +func (db *DB) maxIdleConns() int { + const defaultMaxIdleConns = 2 + // TODO(bradfitz): ask driver, if supported, for its default preference + // TODO(bradfitz): let users override? + return defaultMaxIdleConns +} + +// conn returns a newly-opened or cached driver.Conn +func (db *DB) conn() (driver.Conn, os.Error) { + db.mu.Lock() + if n := len(db.freeConn); n > 0 { + conn := db.freeConn[n-1] + db.freeConn = db.freeConn[:n-1] + db.mu.Unlock() + return conn, nil + } + db.mu.Unlock() + return db.driver.Open(db.dsn) +} + +func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) { + db.mu.Lock() + defer db.mu.Unlock() + for n, conn := range db.freeConn { + if conn == wanted { + db.freeConn[n] = db.freeConn[len(db.freeConn)-1] + db.freeConn = db.freeConn[:len(db.freeConn)-1] + return wanted, true + } + } + return nil, false +} + +func (db *DB) putConn(c driver.Conn) { + if n := len(db.freeConn); n < db.maxIdleConns() { + db.freeConn = append(db.freeConn, c) + return + } + db.closeConn(c) +} + +func (db *DB) closeConn(c driver.Conn) { + // TODO: check to see if we need this Conn for any prepared statements + // that are active. + c.Close() +} + +// Prepare creates a prepared statement for later execution. +func (db *DB) Prepare(query string) (*Stmt, os.Error) { + // TODO: check if db.driver supports an optional + // driver.Preparer interface and call that instead, if so, + // otherwise we make a prepared statement that's bound + // to a connection, and to execute this prepared statement + // we either need to use this connection (if it's free), else + // get a new connection + re-prepare + execute on that one. + ci, err := db.conn() + if err != nil { + return nil, err + } + defer db.putConn(ci) + si, err := ci.Prepare(query) + if err != nil { + return nil, err + } + stmt := &Stmt{ + db: db, + query: query, + css: []connStmt{{ci, si}}, + } + return stmt, nil +} + +// Exec executes a query without returning any rows. +func (db *DB) Exec(query string, args ...interface{}) (Result, os.Error) { + // Optional fast path, if the driver implements driver.Execer. + if execer, ok := db.driver.(driver.Execer); ok { + resi, err := execer.Exec(query, args) + if err != nil { + return nil, err + } + return result{resi}, nil + } + + // If the driver does not implement driver.Execer, we need + // a connection. + conn, err := db.conn() + if err != nil { + return nil, err + } + defer db.putConn(conn) + + if execer, ok := conn.(driver.Execer); ok { + resi, err := execer.Exec(query, args) + if err != nil { + return nil, err + } + return result{resi}, nil + } + + sti, err := conn.Prepare(query) + if err != nil { + return nil, err + } + defer sti.Close() + resi, err := sti.Exec(args) + if err != nil { + return nil, err + } + return result{resi}, nil +} + +// Query executes a query that returns rows, typically a SELECT. +func (db *DB) Query(query string, args ...interface{}) (*Rows, os.Error) { + stmt, err := db.Prepare(query) + if err != nil { + return nil, err + } + defer stmt.Close() + return stmt.Query(args...) +} + +// QueryRow executes a query that is expected to return at most one row. +// QueryRow always return a non-nil value. Errors are deferred until +// Row's Scan method is called. +func (db *DB) QueryRow(query string, args ...interface{}) *Row { + rows, err := db.Query(query, args...) + if err != nil { + return &Row{err: err} + } + return &Row{rows: rows} +} + +// Begin starts a transaction. The isolation level is dependent on +// the driver. +func (db *DB) Begin() (*Tx, os.Error) { + // TODO(bradfitz): add another method for beginning a transaction + // at a specific isolation level. + panic(todo()) +} + +// DriverDatabase returns the database's underlying driver. +func (db *DB) Driver() driver.Driver { + return db.driver +} + +// Tx is an in-progress database transaction. +type Tx struct { + +} + +// Commit commits the transaction. +func (tx *Tx) Commit() os.Error { + panic(todo()) +} + +// Rollback aborts the transaction. +func (tx *Tx) Rollback() os.Error { + panic(todo()) +} + +// Prepare creates a prepared statement. +func (tx *Tx) Prepare(query string) (*Stmt, os.Error) { + panic(todo()) +} + +// Exec executes a query that doesn't return rows. +// For example: an INSERT and UPDATE. +func (tx *Tx) Exec(query string, args ...interface{}) { + panic(todo()) +} + +// Query executes a query that returns rows, typically a SELECT. +func (tx *Tx) Query(query string, args ...interface{}) (*Rows, os.Error) { + panic(todo()) +} + +// QueryRow executes a query that is expected to return at most one row. +// QueryRow always return a non-nil value. Errors are deferred until +// Row's Scan method is called. +func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { + panic(todo()) +} + +// connStmt is a prepared statement on a particular connection. +type connStmt struct { + ci driver.Conn + si driver.Stmt +} + +// Stmt is a prepared statement. Stmt is safe for concurrent use by multiple goroutines. +type Stmt struct { + // Immutable: + db *DB // where we came from + query string // that created the Sttm + + mu sync.Mutex + closed bool + css []connStmt // can use any that have idle connections +} + +func todo() string { + _, file, line, _ := runtime.Caller(1) + return fmt.Sprintf("%s:%d: TODO: implement", file, line) +} + +// Exec executes a prepared statement with the given arguments and +// returns a Result summarizing the effect of the statement. +func (s *Stmt) Exec(args ...interface{}) (Result, os.Error) { + ci, si, err := s.connStmt() + if err != nil { + return nil, err + } + defer s.db.putConn(ci) + + if want := si.NumInput(); len(args) != want { + return nil, fmt.Errorf("db: expected %d arguments, got %d", want, len(args)) + } + + // Convert args to subset types. + if cc, ok := si.(driver.ColumnConverter); ok { + for n, arg := range args { + args[n], err = cc.ColumnConverter(n).ConvertValue(arg) + if err != nil { + return nil, fmt.Errorf("db: converting Exec argument #%d's type: %v", n, err) + } + if !driver.IsParameterSubsetType(args[n]) { + return nil, fmt.Errorf("db: driver ColumnConverter error converted %T to unsupported type %T", + arg, args[n]) + } + } + } else { + for n, arg := range args { + args[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + if err != nil { + return nil, fmt.Errorf("db: converting Exec argument #%d's type: %v", n, err) + } + } + } + + resi, err := si.Exec(args) + if err != nil { + return nil, err + } + return result{resi}, nil +} + +func (s *Stmt) connStmt(args ...interface{}) (driver.Conn, driver.Stmt, os.Error) { + s.mu.Lock() + if s.closed { + return nil, nil, os.NewError("db: statement is closed") + } + var cs connStmt + match := false + for _, v := range s.css { + // TODO(bradfitz): lazily clean up entries in this + // list with dead conns while enumerating + if _, match = s.db.connIfFree(cs.ci); match { + cs = v + break + } + } + s.mu.Unlock() + + // Make a new conn if all are busy. + // TODO(bradfitz): or wait for one? make configurable later? + if !match { + ci, err := s.db.conn() + if err != nil { + return nil, nil, err + } + si, err := ci.Prepare(s.query) + if err != nil { + return nil, nil, err + } + s.mu.Lock() + cs = connStmt{ci, si} + s.css = append(s.css, cs) + s.mu.Unlock() + } + + return cs.ci, cs.si, nil +} + +// Query executes a prepared query statement with the given arguments +// and returns the query results as a *Rows. +func (s *Stmt) Query(args ...interface{}) (*Rows, os.Error) { + ci, si, err := s.connStmt(args...) + if err != nil { + return nil, err + } + if len(args) != si.NumInput() { + return nil, fmt.Errorf("db: statement expects %d inputs; got %d", si.NumInput(), len(args)) + } + rowsi, err := si.Query(args) + if err != nil { + s.db.putConn(ci) + return nil, err + } + // Note: ownership of ci passes to the *Rows + rows := &Rows{ + db: s.db, + ci: ci, + rowsi: rowsi, + } + return rows, nil +} + +// QueryRow executes a prepared query statement with the given arguments. +// If an error occurs during the execution of the statement, that error will +// be returned by a call to Scan on the returned *Row, which is always non-nil. +// If the query selects no rows, the *Row's Scan will return ErrNoRows. +// Otherwise, the *Row's Scan scans the first selected row and discards +// the rest. +// +// Example usage: +// +// var name string +// err := nameByUseridStmt.QueryRow(id).Scan(&s) +func (s *Stmt) QueryRow(args ...interface{}) *Row { + rows, err := s.Query(args...) + if err != nil { + return &Row{err: err} + } + return &Row{rows: rows} +} + +// Close closes the statement. +func (s *Stmt) Close() os.Error { + s.mu.Lock() + defer s.mu.Unlock() // TODO(bradfitz): move this unlock after 'closed = true'? + if s.closed { + return nil + } + s.closed = true + for _, v := range s.css { + if ci, match := s.db.connIfFree(v.ci); match { + v.si.Close() + s.db.putConn(ci) + } else { + // TODO(bradfitz): care that we can't close + // this statement because the statement's + // connection is in use? + } + } + return nil +} + +// Rows is the result of a query. Its cursor starts before the first row +// of the result set. Use Next to advance through the rows: +// +// rows, err := db.Query("SELECT ...") +// ... +// for rows.Next() { +// var id int +// var name string +// err = rows.Scan(&id, &name) +// ... +// } +// err = rows.Error() // get any Error encountered during iteration +// ... +type Rows struct { + db *DB + ci driver.Conn // owned; must be returned when Rows is closed + rowsi driver.Rows + + closed bool + lastcols []interface{} + lasterr os.Error +} + +// Next prepares the next result row for reading with the Scan method. +// It returns true on success, false if there is no next result row. +// Every call to Scan, even the first one, must be preceded by a call +// to Next. +func (rs *Rows) Next() bool { + if rs.closed { + return false + } + if rs.lasterr != nil { + return false + } + if rs.lastcols == nil { + rs.lastcols = make([]interface{}, len(rs.rowsi.Columns())) + } + rs.lasterr = rs.rowsi.Next(rs.lastcols) + return rs.lasterr == nil +} + +// Error returns the error, if any, that was encountered during iteration. +func (rs *Rows) Error() os.Error { + if rs.lasterr == os.EOF { + return nil + } + return rs.lasterr +} + +// Scan copies the columns in the current row into the values pointed +// at by dest. If dest contains pointers to []byte, the slices should +// not be modified and should only be considered valid until the next +// call to Next or Scan. +func (rs *Rows) Scan(dest ...interface{}) os.Error { + if rs.closed { + return os.NewError("db: Rows closed") + } + if rs.lasterr != nil { + return rs.lasterr + } + if rs.lastcols == nil { + return os.NewError("db: Scan called without calling Next") + } + if len(dest) != len(rs.lastcols) { + return fmt.Errorf("db: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest)) + } + for i, sv := range rs.lastcols { + err := convertAssign(dest[i], sv) + if err != nil { + return fmt.Errorf("db: Scan error on column index %d: %v", i, err) + } + } + return nil +} + +// Close closes the Rows, preventing further enumeration. If the +// end is encountered, the Rows are closed automatically. Close +// is idempotent. +func (rs *Rows) Close() os.Error { + if rs.closed { + return nil + } + rs.closed = true + err := rs.rowsi.Close() + rs.db.putConn(rs.ci) + return err +} + +// Row is the result of calling QueryRow to select a single row. +type Row struct { + // One of these two will be non-nil: + err os.Error // deferred error for easy chaining + rows *Rows +} + +// Scan copies the columns from the matched row into the values +// pointed at by dest. If more than one row matches the query, +// Scan uses the first row and discards the rest. If no row matches +// the query, Scan returns ErrNoRows. +// +// If dest contains pointers to []byte, the slices should not be +// modified and should only be considered valid until the next call to +// Next or Scan. +func (r *Row) Scan(dest ...interface{}) os.Error { + if r.err != nil { + return r.err + } + defer r.rows.Close() + if !r.rows.Next() { + return ErrNoRows + } + return r.rows.Scan(dest...) +} + +// A Result summarizes an executed SQL command. +type Result interface { + LastInsertId() (int64, os.Error) + RowsAffected() (int64, os.Error) +} + +type result struct { + driver.Result +} diff --git a/libgo/go/exp/sql/sql_test.go b/libgo/go/exp/sql/sql_test.go new file mode 100644 index 00000000000..eaa0a90356b --- /dev/null +++ b/libgo/go/exp/sql/sql_test.go @@ -0,0 +1,145 @@ +// 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 sql + +import ( + "strings" + "testing" +) + +func newTestDB(t *testing.T, name string) *DB { + db, err := Open("test", "foo") + if err != nil { + t.Fatalf("Open: %v", err) + } + if _, err := db.Exec("WIPE"); err != nil { + t.Fatalf("exec wipe: %v", err) + } + if name == "people" { + exec(t, db, "CREATE|people|name=string,age=int32,dead=bool") + exec(t, db, "INSERT|people|name=Alice,age=?", 1) + exec(t, db, "INSERT|people|name=Bob,age=?", 2) + exec(t, db, "INSERT|people|name=Chris,age=?", 3) + + } + return db +} + +func exec(t *testing.T, db *DB, query string, args ...interface{}) { + _, err := db.Exec(query, args...) + if err != nil { + t.Fatalf("Exec of %q: %v", query, err) + } +} + +func TestQuery(t *testing.T) { + db := newTestDB(t, "people") + var name string + var age int + + err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age) + if err == nil || !strings.Contains(err.String(), "expected 2 destination arguments") { + t.Errorf("expected error from wrong number of arguments; actually got: %v", err) + } + + err = db.QueryRow("SELECT|people|age,name|age=?", 2).Scan(&age, &name) + if err != nil { + t.Fatalf("age QueryRow+Scan: %v", err) + } + if name != "Bob" { + t.Errorf("expected name Bob, got %q", name) + } + if age != 2 { + t.Errorf("expected age 2, got %d", age) + } + + err = db.QueryRow("SELECT|people|age,name|name=?", "Alice").Scan(&age, &name) + if err != nil { + t.Fatalf("name QueryRow+Scan: %v", err) + } + if name != "Alice" { + t.Errorf("expected name Alice, got %q", name) + } + if age != 1 { + t.Errorf("expected age 1, got %d", age) + } +} + +func TestStatementQueryRow(t *testing.T) { + db := newTestDB(t, "people") + stmt, err := db.Prepare("SELECT|people|age|name=?") + if err != nil { + t.Fatalf("Prepare: %v", err) + } + var age int + for n, tt := range []struct { + name string + want int + }{ + {"Alice", 1}, + {"Bob", 2}, + {"Chris", 3}, + } { + if err := stmt.QueryRow(tt.name).Scan(&age); err != nil { + t.Errorf("%d: on %q, QueryRow/Scan: %v", n, tt.name, err) + } else if age != tt.want { + t.Errorf("%d: age=%d, want %d", n, age, tt.want) + } + } + +} + +// just a test of fakedb itself +func TestBogusPreboundParameters(t *testing.T) { + db := newTestDB(t, "foo") + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + _, err := db.Prepare("INSERT|t1|name=?,age=bogusconversion") + if err == nil { + t.Fatalf("expected error") + } + if err.String() != `fakedb: invalid conversion to int32 from "bogusconversion"` { + t.Errorf("unexpected error: %v", err) + } +} + +func TestDb(t *testing.T) { + db := newTestDB(t, "foo") + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + stmt, err := db.Prepare("INSERT|t1|name=?,age=?") + if err != nil { + t.Errorf("Stmt, err = %v, %v", stmt, err) + } + + type execTest struct { + args []interface{} + wantErr string + } + execTests := []execTest{ + // Okay: + {[]interface{}{"Brad", 31}, ""}, + {[]interface{}{"Brad", int64(31)}, ""}, + {[]interface{}{"Bob", "32"}, ""}, + {[]interface{}{7, 9}, ""}, + + // Invalid conversions: + {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "db: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"}, + {[]interface{}{"Brad", "strconv fail"}, "db: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"}, + + // Wrong number of args: + {[]interface{}{}, "db: expected 2 arguments, got 0"}, + {[]interface{}{1, 2, 3}, "db: expected 2 arguments, got 3"}, + } + for n, et := range execTests { + _, err := stmt.Exec(et.args...) + errStr := "" + if err != nil { + errStr = err.String() + } + if errStr != et.wantErr { + t.Errorf("stmt.Execute #%d: for %v, got error %q, want error %q", + n, et.args, errStr, et.wantErr) + } + } +} diff --git a/libgo/go/exp/ssh/channel.go b/libgo/go/exp/ssh/channel.go new file mode 100644 index 00000000000..922584f6317 --- /dev/null +++ b/libgo/go/exp/ssh/channel.go @@ -0,0 +1,317 @@ +// 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 ssh + +import ( + "os" + "sync" +) + +// A Channel is an ordered, reliable, duplex stream that is multiplexed over an +// SSH connection. +type Channel interface { + // Accept accepts the channel creation request. + Accept() os.Error + // Reject rejects the channel creation request. After calling this, no + // other methods on the Channel may be called. If they are then the + // peer is likely to signal a protocol error and drop the connection. + Reject(reason RejectionReason, message string) os.Error + + // Read may return a ChannelRequest as an os.Error. + Read(data []byte) (int, os.Error) + Write(data []byte) (int, os.Error) + Close() os.Error + + // AckRequest either sends an ack or nack to the channel request. + AckRequest(ok bool) os.Error + + // ChannelType returns the type of the channel, as supplied by the + // client. + ChannelType() string + // ExtraData returns the arbitary payload for this channel, as supplied + // by the client. This data is specific to the channel type. + ExtraData() []byte +} + +// ChannelRequest represents a request sent on a channel, outside of the normal +// stream of bytes. It may result from calling Read on a Channel. +type ChannelRequest struct { + Request string + WantReply bool + Payload []byte +} + +func (c ChannelRequest) String() string { + return "channel request received" +} + +// RejectionReason is an enumeration used when rejecting channel creation +// requests. See RFC 4254, section 5.1. +type RejectionReason int + +const ( + Prohibited RejectionReason = iota + 1 + ConnectionFailed + UnknownChannelType + ResourceShortage +) + +type channel struct { + // immutable once created + chanType string + extraData []byte + + theyClosed bool + theySentEOF bool + weClosed bool + dead bool + + serverConn *ServerConnection + myId, theirId uint32 + myWindow, theirWindow uint32 + maxPacketSize uint32 + err os.Error + + pendingRequests []ChannelRequest + pendingData []byte + head, length int + + // This lock is inferior to serverConn.lock + lock sync.Mutex + cond *sync.Cond +} + +func (c *channel) Accept() os.Error { + c.serverConn.lock.Lock() + defer c.serverConn.lock.Unlock() + + if c.serverConn.err != nil { + return c.serverConn.err + } + + confirm := channelOpenConfirmMsg{ + PeersId: c.theirId, + MyId: c.myId, + MyWindow: c.myWindow, + MaxPacketSize: c.maxPacketSize, + } + return c.serverConn.writePacket(marshal(msgChannelOpenConfirm, confirm)) +} + +func (c *channel) Reject(reason RejectionReason, message string) os.Error { + c.serverConn.lock.Lock() + defer c.serverConn.lock.Unlock() + + if c.serverConn.err != nil { + return c.serverConn.err + } + + reject := channelOpenFailureMsg{ + PeersId: c.theirId, + Reason: uint32(reason), + Message: message, + Language: "en", + } + return c.serverConn.writePacket(marshal(msgChannelOpenFailure, reject)) +} + +func (c *channel) handlePacket(packet interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + + switch packet := packet.(type) { + case *channelRequestMsg: + req := ChannelRequest{ + Request: packet.Request, + WantReply: packet.WantReply, + Payload: packet.RequestSpecificData, + } + + c.pendingRequests = append(c.pendingRequests, req) + c.cond.Signal() + case *channelCloseMsg: + c.theyClosed = true + c.cond.Signal() + case *channelEOFMsg: + c.theySentEOF = true + c.cond.Signal() + default: + panic("unknown packet type") + } +} + +func (c *channel) handleData(data []byte) { + c.lock.Lock() + defer c.lock.Unlock() + + // The other side should never send us more than our window. + if len(data)+c.length > len(c.pendingData) { + // TODO(agl): we should tear down the channel with a protocol + // error. + return + } + + c.myWindow -= uint32(len(data)) + for i := 0; i < 2; i++ { + tail := c.head + c.length + if tail > len(c.pendingData) { + tail -= len(c.pendingData) + } + n := copy(c.pendingData[tail:], data) + data = data[n:] + c.length += n + } + + c.cond.Signal() +} + +func (c *channel) Read(data []byte) (n int, err os.Error) { + c.lock.Lock() + defer c.lock.Unlock() + + if c.err != nil { + return 0, c.err + } + + if c.myWindow <= uint32(len(c.pendingData))/2 { + packet := marshal(msgChannelWindowAdjust, windowAdjustMsg{ + PeersId: c.theirId, + AdditionalBytes: uint32(len(c.pendingData)) - c.myWindow, + }) + if err := c.serverConn.writePacket(packet); err != nil { + return 0, err + } + } + + for { + if c.theySentEOF || c.theyClosed || c.dead { + return 0, os.EOF + } + + if len(c.pendingRequests) > 0 { + req := c.pendingRequests[0] + if len(c.pendingRequests) == 1 { + c.pendingRequests = nil + } else { + oldPendingRequests := c.pendingRequests + c.pendingRequests = make([]ChannelRequest, len(oldPendingRequests)-1) + copy(c.pendingRequests, oldPendingRequests[1:]) + } + + return 0, req + } + + if c.length > 0 { + tail := c.head + c.length + if tail > len(c.pendingData) { + tail -= len(c.pendingData) + } + n = copy(data, c.pendingData[c.head:tail]) + c.head += n + c.length -= n + if c.head == len(c.pendingData) { + c.head = 0 + } + return + } + + c.cond.Wait() + } + + panic("unreachable") +} + +func (c *channel) Write(data []byte) (n int, err os.Error) { + for len(data) > 0 { + c.lock.Lock() + if c.dead || c.weClosed { + return 0, os.EOF + } + + if c.theirWindow == 0 { + c.cond.Wait() + continue + } + c.lock.Unlock() + + todo := data + if uint32(len(todo)) > c.theirWindow { + todo = todo[:c.theirWindow] + } + + packet := make([]byte, 1+4+4+len(todo)) + packet[0] = msgChannelData + packet[1] = byte(c.theirId) >> 24 + packet[2] = byte(c.theirId) >> 16 + packet[3] = byte(c.theirId) >> 8 + packet[4] = byte(c.theirId) + packet[5] = byte(len(todo)) >> 24 + packet[6] = byte(len(todo)) >> 16 + packet[7] = byte(len(todo)) >> 8 + packet[8] = byte(len(todo)) + copy(packet[9:], todo) + + c.serverConn.lock.Lock() + if err = c.serverConn.writePacket(packet); err != nil { + c.serverConn.lock.Unlock() + return + } + c.serverConn.lock.Unlock() + + n += len(todo) + data = data[len(todo):] + } + + return +} + +func (c *channel) Close() os.Error { + c.serverConn.lock.Lock() + defer c.serverConn.lock.Unlock() + + if c.serverConn.err != nil { + return c.serverConn.err + } + + if c.weClosed { + return os.NewError("ssh: channel already closed") + } + c.weClosed = true + + closeMsg := channelCloseMsg{ + PeersId: c.theirId, + } + return c.serverConn.writePacket(marshal(msgChannelClose, closeMsg)) +} + +func (c *channel) AckRequest(ok bool) os.Error { + c.serverConn.lock.Lock() + defer c.serverConn.lock.Unlock() + + if c.serverConn.err != nil { + return c.serverConn.err + } + + if ok { + ack := channelRequestSuccessMsg{ + PeersId: c.theirId, + } + return c.serverConn.writePacket(marshal(msgChannelSuccess, ack)) + } else { + ack := channelRequestFailureMsg{ + PeersId: c.theirId, + } + return c.serverConn.writePacket(marshal(msgChannelFailure, ack)) + } + panic("unreachable") +} + +func (c *channel) ChannelType() string { + return c.chanType +} + +func (c *channel) ExtraData() []byte { + return c.extraData +} diff --git a/libgo/go/exp/ssh/common.go b/libgo/go/exp/ssh/common.go new file mode 100644 index 00000000000..739bd2f9c5f --- /dev/null +++ b/libgo/go/exp/ssh/common.go @@ -0,0 +1,129 @@ +// 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 ssh + +import ( + "big" + "strconv" + "sync" +) + +// These are string constants in the SSH protocol. +const ( + kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" + hostAlgoRSA = "ssh-rsa" + cipherAES128CTR = "aes128-ctr" + macSHA196 = "hmac-sha1-96" + compressionNone = "none" + serviceUserAuth = "ssh-userauth" + serviceSSH = "ssh-connection" +) + +var supportedKexAlgos = []string{kexAlgoDH14SHA1} +var supportedHostKeyAlgos = []string{hostAlgoRSA} +var supportedCiphers = []string{cipherAES128CTR} +var supportedMACs = []string{macSHA196} +var supportedCompressions = []string{compressionNone} + +// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. +type dhGroup struct { + g, p *big.Int +} + +// dhGroup14 is the group called diffie-hellman-group14-sha1 in RFC 4253 and +// Oakley Group 14 in RFC 3526. +var dhGroup14 *dhGroup + +var dhGroup14Once sync.Once + +func initDHGroup14() { + p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) + + dhGroup14 = &dhGroup{ + g: new(big.Int).SetInt64(2), + p: p, + } +} + +// UnexpectedMessageError results when the SSH message that we received didn't +// match what we wanted. +type UnexpectedMessageError struct { + expected, got uint8 +} + +func (u UnexpectedMessageError) String() string { + return "ssh: unexpected message type " + strconv.Itoa(int(u.got)) + " (expected " + strconv.Itoa(int(u.expected)) + ")" +} + +// ParseError results from a malformed SSH message. +type ParseError struct { + msgType uint8 +} + +func (p ParseError) String() string { + return "ssh: parse error in message type " + strconv.Itoa(int(p.msgType)) +} + +type handshakeMagics struct { + clientVersion, serverVersion []byte + clientKexInit, serverKexInit []byte +} + +func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) { + for _, clientAlgo := range clientAlgos { + for _, serverAlgo := range serverAlgos { + if clientAlgo == serverAlgo { + return clientAlgo, true + } + } + } + + return +} + +func findAgreedAlgorithms(transport *transport, clientKexInit, serverKexInit *kexInitMsg) (kexAlgo, hostKeyAlgo string, ok bool) { + kexAlgo, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos) + if !ok { + return + } + + hostKeyAlgo, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) + if !ok { + return + } + + transport.writer.cipherAlgo, ok = findCommonAlgorithm(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) + if !ok { + return + } + + transport.reader.cipherAlgo, ok = findCommonAlgorithm(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) + if !ok { + return + } + + transport.writer.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) + if !ok { + return + } + + transport.reader.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) + if !ok { + return + } + + transport.writer.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) + if !ok { + return + } + + transport.reader.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) + if !ok { + return + } + + ok = true + return +} diff --git a/libgo/go/exp/ssh/doc.go b/libgo/go/exp/ssh/doc.go new file mode 100644 index 00000000000..54a7ba9fdae --- /dev/null +++ b/libgo/go/exp/ssh/doc.go @@ -0,0 +1,79 @@ +// 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 ssh implements an SSH server. + +SSH is a transport security protocol, an authentication protocol and a +family of application protocols. The most typical application level +protocol is a remote shell and this is specifically implemented. However, +the multiplexed nature of SSH is exposed to users that wish to support +others. + +An SSH server is represented by a Server, which manages a number of +ServerConnections and handles authentication. + + var s Server + s.PubKeyCallback = pubKeyAuth + s.PasswordCallback = passwordAuth + + pemBytes, err := ioutil.ReadFile("id_rsa") + if err != nil { + panic("Failed to load private key") + } + err = s.SetRSAPrivateKey(pemBytes) + if err != nil { + panic("Failed to parse private key") + } + +Once a Server has been set up, connections can be attached. + + var sConn ServerConnection + sConn.Server = &s + err = sConn.Handshake(conn) + if err != nil { + panic("failed to handshake") + } + +An SSH connection multiplexes several channels, which must be accepted themselves: + + + for { + channel, err := sConn.Accept() + if err != nil { + panic("error from Accept") + } + + ... + } + +Accept reads from the connection, demultiplexes packets to their corresponding +channels and returns when a new channel request is seen. Some goroutine must +always be calling Accept; otherwise no messages will be forwarded to the +channels. + +Channels have a type, depending on the application level protocol intended. In +the case of a shell, the type is "session" and ServerShell may be used to +present a simple terminal interface. + + if channel.ChannelType() != "session" { + c.Reject(UnknownChannelType, "unknown channel type") + return + } + channel.Accept() + + shell := NewServerShell(channel, "> ") + go func() { + defer channel.Close() + for { + line, err := shell.ReadLine() + if err != nil { + break + } + println(line) + } + return + }() +*/ +package ssh diff --git a/libgo/go/exp/ssh/messages.go b/libgo/go/exp/ssh/messages.go new file mode 100644 index 00000000000..1d0bc577426 --- /dev/null +++ b/libgo/go/exp/ssh/messages.go @@ -0,0 +1,636 @@ +// 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 ssh + +import ( + "big" + "bytes" + "io" + "os" + "reflect" +) + +// These are SSH message type numbers. They are scattered around several +// documents but many were taken from +// http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 +const ( + msgDisconnect = 1 + msgIgnore = 2 + msgUnimplemented = 3 + msgDebug = 4 + msgServiceRequest = 5 + msgServiceAccept = 6 + + msgKexInit = 20 + msgNewKeys = 21 + + msgKexDHInit = 30 + msgKexDHReply = 31 + + msgUserAuthRequest = 50 + msgUserAuthFailure = 51 + msgUserAuthSuccess = 52 + msgUserAuthBanner = 53 + msgUserAuthPubKeyOk = 60 + + msgGlobalRequest = 80 + msgRequestSuccess = 81 + msgRequestFailure = 82 + + msgChannelOpen = 90 + msgChannelOpenConfirm = 91 + msgChannelOpenFailure = 92 + msgChannelWindowAdjust = 93 + msgChannelData = 94 + msgChannelExtendedData = 95 + msgChannelEOF = 96 + msgChannelClose = 97 + msgChannelRequest = 98 + msgChannelSuccess = 99 + msgChannelFailure = 100 +) + +// SSH messages: +// +// These structures mirror the wire format of the corresponding SSH messages. +// They are marshaled using reflection with the marshal and unmarshal functions +// in this file. The only wrinkle is that a final member of type []byte with a +// ssh tag of "rest" receives the remainder of a packet when unmarshaling. + +// See RFC 4253, section 11.1. +type disconnectMsg struct { + Reason uint32 + Message string + Language string +} + +// See RFC 4253, section 7.1. +type kexInitMsg struct { + Cookie [16]byte + KexAlgos []string + ServerHostKeyAlgos []string + CiphersClientServer []string + CiphersServerClient []string + MACsClientServer []string + MACsServerClient []string + CompressionClientServer []string + CompressionServerClient []string + LanguagesClientServer []string + LanguagesServerClient []string + FirstKexFollows bool + Reserved uint32 +} + +// See RFC 4253, section 8. +type kexDHInitMsg struct { + X *big.Int +} + +type kexDHReplyMsg struct { + HostKey []byte + Y *big.Int + Signature []byte +} + +// See RFC 4253, section 10. +type serviceRequestMsg struct { + Service string +} + +// See RFC 4253, section 10. +type serviceAcceptMsg struct { + Service string +} + +// See RFC 4252, section 5. +type userAuthRequestMsg struct { + User string + Service string + Method string + Payload []byte `ssh:"rest"` +} + +// See RFC 4252, section 5.1 +type userAuthFailureMsg struct { + Methods []string + PartialSuccess bool +} + +// See RFC 4254, section 5.1. +type channelOpenMsg struct { + ChanType string + PeersId uint32 + PeersWindow uint32 + MaxPacketSize uint32 + TypeSpecificData []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.1. +type channelOpenConfirmMsg struct { + PeersId uint32 + MyId uint32 + MyWindow uint32 + MaxPacketSize uint32 + TypeSpecificData []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.1. +type channelOpenFailureMsg struct { + PeersId uint32 + Reason uint32 + Message string + Language string +} + +// See RFC 4254, section 5.2. +type channelData struct { + PeersId uint32 + Payload []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.2. +type channelExtendedData struct { + PeersId uint32 + Datatype uint32 + Data string +} + +type channelRequestMsg struct { + PeersId uint32 + Request string + WantReply bool + RequestSpecificData []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.4. +type channelRequestSuccessMsg struct { + PeersId uint32 +} + +// See RFC 4254, section 5.4. +type channelRequestFailureMsg struct { + PeersId uint32 +} + +// See RFC 4254, section 5.3 +type channelCloseMsg struct { + PeersId uint32 +} + +// See RFC 4254, section 5.3 +type channelEOFMsg struct { + PeersId uint32 +} + +// See RFC 4254, section 4 +type globalRequestMsg struct { + Type string + WantReply bool +} + +// See RFC 4254, section 5.2 +type windowAdjustMsg struct { + PeersId uint32 + AdditionalBytes uint32 +} + +// See RFC 4252, section 7 +type userAuthPubKeyOkMsg struct { + Algo string + PubKey string +} + +// unmarshal parses the SSH wire data in packet into out using reflection. +// expectedType is the expected SSH message type. It either returns nil on +// success, or a ParseError or UnexpectedMessageError on error. +func unmarshal(out interface{}, packet []byte, expectedType uint8) os.Error { + if len(packet) == 0 { + return ParseError{expectedType} + } + if packet[0] != expectedType { + return UnexpectedMessageError{expectedType, packet[0]} + } + packet = packet[1:] + + v := reflect.ValueOf(out).Elem() + structType := v.Type() + var ok bool + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + t := field.Type() + switch t.Kind() { + case reflect.Bool: + if len(packet) < 1 { + return ParseError{expectedType} + } + field.SetBool(packet[0] != 0) + packet = packet[1:] + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 { + panic("array of non-uint8") + } + if len(packet) < t.Len() { + return ParseError{expectedType} + } + for j := 0; j < t.Len(); j++ { + field.Index(j).Set(reflect.ValueOf(packet[j])) + } + packet = packet[t.Len():] + case reflect.Uint32: + var u32 uint32 + if u32, packet, ok = parseUint32(packet); !ok { + return ParseError{expectedType} + } + field.SetUint(uint64(u32)) + case reflect.String: + var s []byte + if s, packet, ok = parseString(packet); !ok { + return ParseError{expectedType} + } + field.SetString(string(s)) + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Uint8: + if structType.Field(i).Tag.Get("ssh") == "rest" { + field.Set(reflect.ValueOf(packet)) + packet = nil + } else { + var s []byte + if s, packet, ok = parseString(packet); !ok { + return ParseError{expectedType} + } + field.Set(reflect.ValueOf(s)) + } + case reflect.String: + var nl []string + if nl, packet, ok = parseNameList(packet); !ok { + return ParseError{expectedType} + } + field.Set(reflect.ValueOf(nl)) + default: + panic("slice of unknown type") + } + case reflect.Ptr: + if t == bigIntType { + var n *big.Int + if n, packet, ok = parseInt(packet); !ok { + return ParseError{expectedType} + } + field.Set(reflect.ValueOf(n)) + } else { + panic("pointer to unknown type") + } + default: + panic("unknown type") + } + } + + if len(packet) != 0 { + return ParseError{expectedType} + } + + return nil +} + +// marshal serializes the message in msg, using the given message type. +func marshal(msgType uint8, msg interface{}) []byte { + var out []byte + out = append(out, msgType) + + v := reflect.ValueOf(msg) + structType := v.Type() + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + t := field.Type() + switch t.Kind() { + case reflect.Bool: + var v uint8 + if field.Bool() { + v = 1 + } + out = append(out, v) + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 { + panic("array of non-uint8") + } + for j := 0; j < t.Len(); j++ { + out = append(out, byte(field.Index(j).Uint())) + } + case reflect.Uint32: + u32 := uint32(field.Uint()) + out = append(out, byte(u32>>24)) + out = append(out, byte(u32>>16)) + out = append(out, byte(u32>>8)) + out = append(out, byte(u32)) + case reflect.String: + s := field.String() + out = append(out, byte(len(s)>>24)) + out = append(out, byte(len(s)>>16)) + out = append(out, byte(len(s)>>8)) + out = append(out, byte(len(s))) + out = append(out, s...) + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Uint8: + length := field.Len() + if structType.Field(i).Tag.Get("ssh") != "rest" { + out = append(out, byte(length>>24)) + out = append(out, byte(length>>16)) + out = append(out, byte(length>>8)) + out = append(out, byte(length)) + } + for j := 0; j < length; j++ { + out = append(out, byte(field.Index(j).Uint())) + } + case reflect.String: + var length int + for j := 0; j < field.Len(); j++ { + if j != 0 { + length++ /* comma */ + } + length += len(field.Index(j).String()) + } + + out = append(out, byte(length>>24)) + out = append(out, byte(length>>16)) + out = append(out, byte(length>>8)) + out = append(out, byte(length)) + for j := 0; j < field.Len(); j++ { + if j != 0 { + out = append(out, ',') + } + out = append(out, field.Index(j).String()...) + } + default: + panic("slice of unknown type") + } + case reflect.Ptr: + if t == bigIntType { + var n *big.Int + nValue := reflect.ValueOf(&n) + nValue.Elem().Set(field) + needed := intLength(n) + oldLength := len(out) + + if cap(out)-len(out) < needed { + newOut := make([]byte, len(out), 2*(len(out)+needed)) + copy(newOut, out) + out = newOut + } + out = out[:oldLength+needed] + marshalInt(out[oldLength:], n) + } else { + panic("pointer to unknown type") + } + } + } + + return out +} + +var bigOne = big.NewInt(1) + +func parseString(in []byte) (out, rest []byte, ok bool) { + if len(in) < 4 { + return + } + length := uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3]) + if uint32(len(in)) < 4+length { + return + } + out = in[4 : 4+length] + rest = in[4+length:] + ok = true + return +} + +var comma = []byte{','} + +func parseNameList(in []byte) (out []string, rest []byte, ok bool) { + contents, rest, ok := parseString(in) + if !ok { + return + } + if len(contents) == 0 { + return + } + parts := bytes.Split(contents, comma) + out = make([]string, len(parts)) + for i, part := range parts { + out[i] = string(part) + } + return +} + +func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) { + contents, rest, ok := parseString(in) + if !ok { + return + } + out = new(big.Int) + + if len(contents) > 0 && contents[0]&0x80 == 0x80 { + // This is a negative number + notBytes := make([]byte, len(contents)) + for i := range notBytes { + notBytes[i] = ^contents[i] + } + out.SetBytes(notBytes) + out.Add(out, bigOne) + out.Neg(out) + } else { + // Positive number + out.SetBytes(contents) + } + ok = true + return +} + +func parseUint32(in []byte) (out uint32, rest []byte, ok bool) { + if len(in) < 4 { + return + } + out = uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3]) + rest = in[4:] + ok = true + return +} + +const maxPacketSize = 36000 + +func nameListLength(namelist []string) int { + length := 4 /* uint32 length prefix */ + for i, name := range namelist { + if i != 0 { + length++ /* comma */ + } + length += len(name) + } + return length +} + +func intLength(n *big.Int) int { + length := 4 /* length bytes */ + if n.Sign() < 0 { + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bitLen := nMinus1.BitLen() + if bitLen%8 == 0 { + // The number will need 0xff padding + length++ + } + length += (bitLen + 7) / 8 + } else if n.Sign() == 0 { + // A zero is the zero length string + } else { + bitLen := n.BitLen() + if bitLen%8 == 0 { + // The number will need 0x00 padding + length++ + } + length += (bitLen + 7) / 8 + } + + return length +} + +func marshalInt(to []byte, n *big.Int) []byte { + lengthBytes := to + to = to[4:] + length := 0 + + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement + // form. So we'll subtract 1 and invert. If the + // most-significant-bit isn't set then we'll need to pad the + // beginning with 0xff in order to keep the number negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if len(bytes) == 0 || bytes[0]&0x80 == 0 { + to[0] = 0xff + to = to[1:] + length++ + } + nBytes := copy(to, bytes) + to = to[nBytes:] + length += nBytes + } else if n.Sign() == 0 { + // A zero is the zero length string + } else { + bytes := n.Bytes() + if len(bytes) > 0 && bytes[0]&0x80 != 0 { + // We'll have to pad this with a 0x00 in order to + // stop it looking like a negative number. + to[0] = 0 + to = to[1:] + length++ + } + nBytes := copy(to, bytes) + to = to[nBytes:] + length += nBytes + } + + lengthBytes[0] = byte(length >> 24) + lengthBytes[1] = byte(length >> 16) + lengthBytes[2] = byte(length >> 8) + lengthBytes[3] = byte(length) + return to +} + +func writeInt(w io.Writer, n *big.Int) { + length := intLength(n) + buf := make([]byte, length) + marshalInt(buf, n) + w.Write(buf) +} + +func writeString(w io.Writer, s []byte) { + var lengthBytes [4]byte + lengthBytes[0] = byte(len(s) >> 24) + lengthBytes[1] = byte(len(s) >> 16) + lengthBytes[2] = byte(len(s) >> 8) + lengthBytes[3] = byte(len(s)) + w.Write(lengthBytes[:]) + w.Write(s) +} + +func stringLength(s []byte) int { + return 4 + len(s) +} + +func marshalString(to []byte, s []byte) []byte { + to[0] = byte(len(s) >> 24) + to[1] = byte(len(s) >> 16) + to[2] = byte(len(s) >> 8) + to[3] = byte(len(s)) + to = to[4:] + copy(to, s) + return to[len(s):] +} + +var bigIntType = reflect.TypeOf((*big.Int)(nil)) + +// Decode a packet into it's corresponding message. +func decode(packet []byte) interface{} { + var msg interface{} + switch packet[0] { + case msgDisconnect: + msg = new(disconnectMsg) + case msgServiceRequest: + msg = new(serviceRequestMsg) + case msgServiceAccept: + msg = new(serviceAcceptMsg) + case msgKexInit: + msg = new(kexInitMsg) + case msgKexDHInit: + msg = new(kexDHInitMsg) + case msgKexDHReply: + msg = new(kexDHReplyMsg) + case msgUserAuthRequest: + msg = new(userAuthRequestMsg) + case msgUserAuthFailure: + msg = new(userAuthFailureMsg) + case msgUserAuthPubKeyOk: + msg = new(userAuthPubKeyOkMsg) + case msgGlobalRequest: + msg = new(globalRequestMsg) + case msgRequestSuccess: + msg = new(channelRequestSuccessMsg) + case msgRequestFailure: + msg = new(channelRequestFailureMsg) + case msgChannelOpen: + msg = new(channelOpenMsg) + case msgChannelOpenConfirm: + msg = new(channelOpenConfirmMsg) + case msgChannelOpenFailure: + msg = new(channelOpenFailureMsg) + case msgChannelWindowAdjust: + msg = new(windowAdjustMsg) + case msgChannelData: + msg = new(channelData) + case msgChannelExtendedData: + msg = new(channelExtendedData) + case msgChannelEOF: + msg = new(channelEOFMsg) + case msgChannelClose: + msg = new(channelCloseMsg) + case msgChannelRequest: + msg = new(channelRequestMsg) + case msgChannelSuccess: + msg = new(channelRequestSuccessMsg) + case msgChannelFailure: + msg = new(channelRequestFailureMsg) + default: + return UnexpectedMessageError{0, packet[0]} + } + if err := unmarshal(msg, packet, packet[0]); err != nil { + return err + } + return msg +} diff --git a/libgo/go/exp/ssh/messages_test.go b/libgo/go/exp/ssh/messages_test.go new file mode 100644 index 00000000000..629f3d3b145 --- /dev/null +++ b/libgo/go/exp/ssh/messages_test.go @@ -0,0 +1,125 @@ +// 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 ssh + +import ( + "big" + "rand" + "reflect" + "testing" + "testing/quick" +) + +var intLengthTests = []struct { + val, length int +}{ + {0, 4 + 0}, + {1, 4 + 1}, + {127, 4 + 1}, + {128, 4 + 2}, + {-1, 4 + 1}, +} + +func TestIntLength(t *testing.T) { + for _, test := range intLengthTests { + v := new(big.Int).SetInt64(int64(test.val)) + length := intLength(v) + if length != test.length { + t.Errorf("For %d, got length %d but expected %d", test.val, length, test.length) + } + } +} + +var messageTypes = []interface{}{ + &kexInitMsg{}, + &kexDHInitMsg{}, + &serviceRequestMsg{}, + &serviceAcceptMsg{}, + &userAuthRequestMsg{}, + &channelOpenMsg{}, + &channelOpenConfirmMsg{}, + &channelRequestMsg{}, + &channelRequestSuccessMsg{}, +} + +func TestMarshalUnmarshal(t *testing.T) { + rand := rand.New(rand.NewSource(0)) + for i, iface := range messageTypes { + ty := reflect.ValueOf(iface).Type() + + n := 100 + if testing.Short() { + n = 5 + } + for j := 0; j < n; j++ { + v, ok := quick.Value(ty, rand) + if !ok { + t.Errorf("#%d: failed to create value", i) + break + } + + m1 := v.Elem().Interface() + m2 := iface + + marshaled := marshal(msgIgnore, m1) + if err := unmarshal(m2, marshaled, msgIgnore); err != nil { + t.Errorf("#%d failed to unmarshal %#v: %s", i, m1, err) + break + } + + if !reflect.DeepEqual(v.Interface(), m2) { + t.Errorf("#%d\ngot: %#v\nwant:%#v\n%x", i, m2, m1, marshaled) + break + } + } + } +} + +func randomBytes(out []byte, rand *rand.Rand) { + for i := 0; i < len(out); i++ { + out[i] = byte(rand.Int31()) + } +} + +func randomNameList(rand *rand.Rand) []string { + ret := make([]string, rand.Int31()&15) + for i := range ret { + s := make([]byte, 1+(rand.Int31()&15)) + for j := range s { + s[j] = 'a' + uint8(rand.Int31()&15) + } + ret[i] = string(s) + } + return ret +} + +func randomInt(rand *rand.Rand) *big.Int { + return new(big.Int).SetInt64(int64(int32(rand.Uint32()))) +} + +func (*kexInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { + ki := &kexInitMsg{} + randomBytes(ki.Cookie[:], rand) + ki.KexAlgos = randomNameList(rand) + ki.ServerHostKeyAlgos = randomNameList(rand) + ki.CiphersClientServer = randomNameList(rand) + ki.CiphersServerClient = randomNameList(rand) + ki.MACsClientServer = randomNameList(rand) + ki.MACsServerClient = randomNameList(rand) + ki.CompressionClientServer = randomNameList(rand) + ki.CompressionServerClient = randomNameList(rand) + ki.LanguagesClientServer = randomNameList(rand) + ki.LanguagesServerClient = randomNameList(rand) + if rand.Int31()&1 == 1 { + ki.FirstKexFollows = true + } + return reflect.ValueOf(ki) +} + +func (*kexDHInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { + dhi := &kexDHInitMsg{} + dhi.X = randomInt(rand) + return reflect.ValueOf(dhi) +} diff --git a/libgo/go/exp/ssh/server.go b/libgo/go/exp/ssh/server.go new file mode 100644 index 00000000000..410cafc44c2 --- /dev/null +++ b/libgo/go/exp/ssh/server.go @@ -0,0 +1,645 @@ +// 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 ssh + +import ( + "big" + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + _ "crypto/sha1" + "crypto/x509" + "encoding/pem" + "net" + "os" + "sync" +) + +// Server represents an SSH server. A Server may have several ServerConnections. +type Server struct { + rsa *rsa.PrivateKey + rsaSerialized []byte + + // NoClientAuth is true if clients are allowed to connect without + // authenticating. + NoClientAuth bool + + // PasswordCallback, if non-nil, is called when a user attempts to + // authenticate using a password. It may be called concurrently from + // several goroutines. + PasswordCallback func(user, password string) bool + + // PubKeyCallback, if non-nil, is called when a client attempts public + // key authentication. It must return true iff the given public key is + // valid for the given user. + PubKeyCallback func(user, algo string, pubkey []byte) bool +} + +// SetRSAPrivateKey sets the private key for a Server. A Server must have a +// private key configured in order to accept connections. The private key must +// be in the form of a PEM encoded, PKCS#1, RSA private key. The file "id_rsa" +// typically contains such a key. +func (s *Server) SetRSAPrivateKey(pemBytes []byte) os.Error { + block, _ := pem.Decode(pemBytes) + if block == nil { + return os.NewError("ssh: no key found") + } + var err os.Error + s.rsa, err = x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return err + } + + s.rsaSerialized = marshalRSA(s.rsa) + return nil +} + +// marshalRSA serializes an RSA private key according to RFC 4256, section 6.6. +func marshalRSA(priv *rsa.PrivateKey) []byte { + e := new(big.Int).SetInt64(int64(priv.E)) + length := stringLength([]byte(hostAlgoRSA)) + length += intLength(e) + length += intLength(priv.N) + + ret := make([]byte, length) + r := marshalString(ret, []byte(hostAlgoRSA)) + r = marshalInt(r, e) + r = marshalInt(r, priv.N) + + return ret +} + +// parseRSA parses an RSA key according to RFC 4256, section 6.6. +func parseRSA(in []byte) (pubKey *rsa.PublicKey, ok bool) { + algo, in, ok := parseString(in) + if !ok || string(algo) != hostAlgoRSA { + return nil, false + } + bigE, in, ok := parseInt(in) + if !ok || bigE.BitLen() > 24 { + return nil, false + } + e := bigE.Int64() + if e < 3 || e&1 == 0 { + return nil, false + } + N, in, ok := parseInt(in) + if !ok || len(in) > 0 { + return nil, false + } + return &rsa.PublicKey{ + N: N, + E: int(e), + }, true +} + +func parseRSASig(in []byte) (sig []byte, ok bool) { + algo, in, ok := parseString(in) + if !ok || string(algo) != hostAlgoRSA { + return nil, false + } + sig, in, ok = parseString(in) + if len(in) > 0 { + ok = false + } + return +} + +// cachedPubKey contains the results of querying whether a public key is +// acceptable for a user. The cache only applies to a single ServerConnection. +type cachedPubKey struct { + user, algo string + pubKey []byte + result bool +} + +const maxCachedPubKeys = 16 + +// ServerConnection represents an incomming connection to a Server. +type ServerConnection struct { + Server *Server + + *transport + + channels map[uint32]*channel + nextChanId uint32 + + // lock protects err and also allows Channels to serialise their writes + // to out. + lock sync.RWMutex + err os.Error + + // cachedPubKeys contains the cache results of tests for public keys. + // Since SSH clients will query whether a public key is acceptable + // before attempting to authenticate with it, we end up with duplicate + // queries for public key validity. + cachedPubKeys []cachedPubKey +} + +// kexDH performs Diffie-Hellman key agreement on a ServerConnection. The +// returned values are given the same names as in RFC 4253, section 8. +func (s *ServerConnection) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) (H, K []byte, err os.Error) { + packet, err := s.readPacket() + if err != nil { + return + } + var kexDHInit kexDHInitMsg + if err = unmarshal(&kexDHInit, packet, msgKexDHInit); err != nil { + return + } + + if kexDHInit.X.Sign() == 0 || kexDHInit.X.Cmp(group.p) >= 0 { + return nil, nil, os.NewError("client DH parameter out of bounds") + } + + y, err := rand.Int(rand.Reader, group.p) + if err != nil { + return + } + + Y := new(big.Int).Exp(group.g, y, group.p) + kInt := new(big.Int).Exp(kexDHInit.X, y, group.p) + + var serializedHostKey []byte + switch hostKeyAlgo { + case hostAlgoRSA: + serializedHostKey = s.Server.rsaSerialized + default: + return nil, nil, os.NewError("internal error") + } + + h := hashFunc.New() + writeString(h, magics.clientVersion) + writeString(h, magics.serverVersion) + writeString(h, magics.clientKexInit) + writeString(h, magics.serverKexInit) + writeString(h, serializedHostKey) + writeInt(h, kexDHInit.X) + writeInt(h, Y) + K = make([]byte, intLength(kInt)) + marshalInt(K, kInt) + h.Write(K) + + H = h.Sum() + + h.Reset() + h.Write(H) + hh := h.Sum() + + var sig []byte + switch hostKeyAlgo { + case hostAlgoRSA: + sig, err = rsa.SignPKCS1v15(rand.Reader, s.Server.rsa, hashFunc, hh) + if err != nil { + return + } + default: + return nil, nil, os.NewError("internal error") + } + + serializedSig := serializeRSASignature(sig) + + kexDHReply := kexDHReplyMsg{ + HostKey: serializedHostKey, + Y: Y, + Signature: serializedSig, + } + packet = marshal(msgKexDHReply, kexDHReply) + + err = s.writePacket(packet) + return +} + +func serializeRSASignature(sig []byte) []byte { + length := stringLength([]byte(hostAlgoRSA)) + length += stringLength(sig) + + ret := make([]byte, length) + r := marshalString(ret, []byte(hostAlgoRSA)) + r = marshalString(r, sig) + + return ret +} + +// serverVersion is the fixed identification string that Server will use. +var serverVersion = []byte("SSH-2.0-Go\r\n") + +// buildDataSignedForAuth returns the data that is signed in order to prove +// posession of a private key. See RFC 4252, section 7. +func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { + user := []byte(req.User) + service := []byte(req.Service) + method := []byte(req.Method) + + length := stringLength(sessionId) + length += 1 + length += stringLength(user) + length += stringLength(service) + length += stringLength(method) + length += 1 + length += stringLength(algo) + length += stringLength(pubKey) + + ret := make([]byte, length) + r := marshalString(ret, sessionId) + r[0] = msgUserAuthRequest + r = r[1:] + r = marshalString(r, user) + r = marshalString(r, service) + r = marshalString(r, method) + r[0] = 1 + r = r[1:] + r = marshalString(r, algo) + r = marshalString(r, pubKey) + return ret +} + +// Handshake performs an SSH transport and client authentication on the given ServerConnection. +func (s *ServerConnection) Handshake(conn net.Conn) os.Error { + var magics handshakeMagics + s.transport = newTransport(conn, rand.Reader) + + if _, err := conn.Write(serverVersion); err != nil { + return err + } + magics.serverVersion = serverVersion[:len(serverVersion)-2] + + version, ok := readVersion(s.transport) + if !ok { + return os.NewError("failed to read version string from client") + } + magics.clientVersion = version + + serverKexInit := kexInitMsg{ + KexAlgos: supportedKexAlgos, + ServerHostKeyAlgos: supportedHostKeyAlgos, + CiphersClientServer: supportedCiphers, + CiphersServerClient: supportedCiphers, + MACsClientServer: supportedMACs, + MACsServerClient: supportedMACs, + CompressionClientServer: supportedCompressions, + CompressionServerClient: supportedCompressions, + } + kexInitPacket := marshal(msgKexInit, serverKexInit) + magics.serverKexInit = kexInitPacket + + if err := s.writePacket(kexInitPacket); err != nil { + return err + } + + packet, err := s.readPacket() + if err != nil { + return err + } + + magics.clientKexInit = packet + + var clientKexInit kexInitMsg + if err = unmarshal(&clientKexInit, packet, msgKexInit); err != nil { + return err + } + + kexAlgo, hostKeyAlgo, ok := findAgreedAlgorithms(s.transport, &clientKexInit, &serverKexInit) + if !ok { + return os.NewError("ssh: no common algorithms") + } + + if clientKexInit.FirstKexFollows && kexAlgo != clientKexInit.KexAlgos[0] { + // The client sent a Kex message for the wrong algorithm, + // which we have to ignore. + _, err := s.readPacket() + if err != nil { + return err + } + } + + var H, K []byte + var hashFunc crypto.Hash + switch kexAlgo { + case kexAlgoDH14SHA1: + hashFunc = crypto.SHA1 + dhGroup14Once.Do(initDHGroup14) + H, K, err = s.kexDH(dhGroup14, hashFunc, &magics, hostKeyAlgo) + default: + err = os.NewError("ssh: internal error") + } + + if err != nil { + return err + } + + packet = []byte{msgNewKeys} + if err = s.writePacket(packet); err != nil { + return err + } + if err = s.transport.writer.setupKeys(serverKeys, K, H, H, hashFunc); err != nil { + return err + } + + if packet, err = s.readPacket(); err != nil { + return err + } + if packet[0] != msgNewKeys { + return UnexpectedMessageError{msgNewKeys, packet[0]} + } + + s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc) + + packet, err = s.readPacket() + if err != nil { + return err + } + + var serviceRequest serviceRequestMsg + if err = unmarshal(&serviceRequest, packet, msgServiceRequest); err != nil { + return err + } + if serviceRequest.Service != serviceUserAuth { + return os.NewError("ssh: requested service '" + serviceRequest.Service + "' before authenticating") + } + + serviceAccept := serviceAcceptMsg{ + Service: serviceUserAuth, + } + packet = marshal(msgServiceAccept, serviceAccept) + if err = s.writePacket(packet); err != nil { + return err + } + + if err = s.authenticate(H); err != nil { + return err + } + + s.channels = make(map[uint32]*channel) + return nil +} + +func isAcceptableAlgo(algo string) bool { + return algo == hostAlgoRSA +} + +// testPubKey returns true if the given public key is acceptable for the user. +func (s *ServerConnection) testPubKey(user, algo string, pubKey []byte) bool { + if s.Server.PubKeyCallback == nil || !isAcceptableAlgo(algo) { + return false + } + + for _, c := range s.cachedPubKeys { + if c.user == user && c.algo == algo && bytes.Equal(c.pubKey, pubKey) { + return c.result + } + } + + result := s.Server.PubKeyCallback(user, algo, pubKey) + if len(s.cachedPubKeys) < maxCachedPubKeys { + c := cachedPubKey{ + user: user, + algo: algo, + pubKey: make([]byte, len(pubKey)), + result: result, + } + copy(c.pubKey, pubKey) + s.cachedPubKeys = append(s.cachedPubKeys, c) + } + + return result +} + +func (s *ServerConnection) authenticate(H []byte) os.Error { + var userAuthReq userAuthRequestMsg + var err os.Error + var packet []byte + +userAuthLoop: + for { + if packet, err = s.readPacket(); err != nil { + return err + } + if err = unmarshal(&userAuthReq, packet, msgUserAuthRequest); err != nil { + return err + } + + if userAuthReq.Service != serviceSSH { + return os.NewError("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service) + } + + switch userAuthReq.Method { + case "none": + if s.Server.NoClientAuth { + break userAuthLoop + } + case "password": + if s.Server.PasswordCallback == nil { + break + } + payload := userAuthReq.Payload + if len(payload) < 1 || payload[0] != 0 { + return ParseError{msgUserAuthRequest} + } + payload = payload[1:] + password, payload, ok := parseString(payload) + if !ok || len(payload) > 0 { + return ParseError{msgUserAuthRequest} + } + + if s.Server.PasswordCallback(userAuthReq.User, string(password)) { + break userAuthLoop + } + case "publickey": + if s.Server.PubKeyCallback == nil { + break + } + payload := userAuthReq.Payload + if len(payload) < 1 { + return ParseError{msgUserAuthRequest} + } + isQuery := payload[0] == 0 + payload = payload[1:] + algoBytes, payload, ok := parseString(payload) + if !ok { + return ParseError{msgUserAuthRequest} + } + algo := string(algoBytes) + + pubKey, payload, ok := parseString(payload) + if !ok { + return ParseError{msgUserAuthRequest} + } + if isQuery { + // The client can query if the given public key + // would be ok. + if len(payload) > 0 { + return ParseError{msgUserAuthRequest} + } + if s.testPubKey(userAuthReq.User, algo, pubKey) { + okMsg := userAuthPubKeyOkMsg{ + Algo: algo, + PubKey: string(pubKey), + } + if err = s.writePacket(marshal(msgUserAuthPubKeyOk, okMsg)); err != nil { + return err + } + continue userAuthLoop + } + } else { + sig, payload, ok := parseString(payload) + if !ok || len(payload) > 0 { + return ParseError{msgUserAuthRequest} + } + if !isAcceptableAlgo(algo) { + break + } + rsaSig, ok := parseRSASig(sig) + if !ok { + return ParseError{msgUserAuthRequest} + } + signedData := buildDataSignedForAuth(H, userAuthReq, algoBytes, pubKey) + switch algo { + case hostAlgoRSA: + hashFunc := crypto.SHA1 + h := hashFunc.New() + h.Write(signedData) + digest := h.Sum() + rsaKey, ok := parseRSA(pubKey) + if !ok { + return ParseError{msgUserAuthRequest} + } + if rsa.VerifyPKCS1v15(rsaKey, hashFunc, digest, rsaSig) != nil { + return ParseError{msgUserAuthRequest} + } + default: + return os.NewError("ssh: isAcceptableAlgo incorrect") + } + if s.testPubKey(userAuthReq.User, algo, pubKey) { + break userAuthLoop + } + } + } + + var failureMsg userAuthFailureMsg + if s.Server.PasswordCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "password") + } + if s.Server.PubKeyCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "publickey") + } + + if len(failureMsg.Methods) == 0 { + return os.NewError("ssh: no authentication methods configured but NoClientAuth is also false") + } + + if err = s.writePacket(marshal(msgUserAuthFailure, failureMsg)); err != nil { + return err + } + } + + packet = []byte{msgUserAuthSuccess} + if err = s.writePacket(packet); err != nil { + return err + } + + return nil +} + +const defaultWindowSize = 32768 + +// Accept reads and processes messages on a ServerConnection. It must be called +// in order to demultiplex messages to any resulting Channels. +func (s *ServerConnection) Accept() (Channel, os.Error) { + if s.err != nil { + return nil, s.err + } + + for { + packet, err := s.readPacket() + if err != nil { + + s.lock.Lock() + s.err = err + s.lock.Unlock() + + for _, c := range s.channels { + c.dead = true + c.handleData(nil) + } + + return nil, err + } + + switch msg := decode(packet).(type) { + case *channelOpenMsg: + c := new(channel) + c.chanType = msg.ChanType + c.theirId = msg.PeersId + c.theirWindow = msg.PeersWindow + c.maxPacketSize = msg.MaxPacketSize + c.extraData = msg.TypeSpecificData + c.myWindow = defaultWindowSize + c.serverConn = s + c.cond = sync.NewCond(&c.lock) + c.pendingData = make([]byte, c.myWindow) + + s.lock.Lock() + c.myId = s.nextChanId + s.nextChanId++ + s.channels[c.myId] = c + s.lock.Unlock() + return c, nil + + case *channelRequestMsg: + s.lock.Lock() + c, ok := s.channels[msg.PeersId] + if !ok { + continue + } + c.handlePacket(msg) + s.lock.Unlock() + + case *channelData: + s.lock.Lock() + c, ok := s.channels[msg.PeersId] + if !ok { + continue + } + c.handleData(msg.Payload) + s.lock.Unlock() + + case *channelEOFMsg: + s.lock.Lock() + c, ok := s.channels[msg.PeersId] + if !ok { + continue + } + c.handlePacket(msg) + s.lock.Unlock() + + case *channelCloseMsg: + s.lock.Lock() + c, ok := s.channels[msg.PeersId] + if !ok { + continue + } + c.handlePacket(msg) + s.lock.Unlock() + + case *globalRequestMsg: + if msg.WantReply { + if err := s.writePacket([]byte{msgRequestFailure}); err != nil { + return nil, err + } + } + + case UnexpectedMessageError: + return nil, msg + case *disconnectMsg: + return nil, os.EOF + default: + // Unknown message. Ignore. + } + } + + panic("unreachable") +} diff --git a/libgo/go/exp/ssh/server_shell.go b/libgo/go/exp/ssh/server_shell.go new file mode 100644 index 00000000000..0e9967a9091 --- /dev/null +++ b/libgo/go/exp/ssh/server_shell.go @@ -0,0 +1,400 @@ +// 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 ssh + +import ( + "os" +) + +// ServerShell contains the state for running a VT100 terminal that is capable +// of reading lines of input. +type ServerShell struct { + c Channel + prompt string + + // line is the current line being entered. + line []byte + // pos is the logical position of the cursor in line + pos int + + // cursorX contains the current X value of the cursor where the left + // edge is 0. cursorY contains the row number where the first row of + // the current line is 0. + cursorX, cursorY int + // maxLine is the greatest value of cursorY so far. + maxLine int + + termWidth, termHeight int + + // outBuf contains the terminal data to be sent. + outBuf []byte + // remainder contains the remainder of any partial key sequences after + // a read. It aliases into inBuf. + remainder []byte + inBuf [256]byte +} + +// NewServerShell runs a VT100 terminal on the given channel. prompt is a +// string that is written at the start of each input line. For example: "> ". +func NewServerShell(c Channel, prompt string) *ServerShell { + return &ServerShell{ + c: c, + prompt: prompt, + termWidth: 80, + termHeight: 24, + } +} + +const ( + keyCtrlD = 4 + keyEnter = '\r' + keyEscape = 27 + keyBackspace = 127 + keyUnknown = 256 + iota + keyUp + keyDown + keyLeft + keyRight + keyAltLeft + keyAltRight +) + +// bytesToKey tries to parse a key sequence from b. If successful, it returns +// the key and the remainder of the input. Otherwise it returns -1. +func bytesToKey(b []byte) (int, []byte) { + if len(b) == 0 { + return -1, nil + } + + if b[0] != keyEscape { + return int(b[0]), b[1:] + } + + if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { + switch b[2] { + case 'A': + return keyUp, b[3:] + case 'B': + return keyDown, b[3:] + case 'C': + return keyRight, b[3:] + case 'D': + return keyLeft, b[3:] + } + } + + if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { + switch b[5] { + case 'C': + return keyAltRight, b[6:] + case 'D': + return keyAltLeft, b[6:] + } + } + + // If we get here then we have a key that we don't recognise, or a + // partial sequence. It's not clear how one should find the end of a + // sequence without knowing them all, but it seems that [a-zA-Z] only + // appears at the end of a sequence. + for i, c := range b[0:] { + if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' { + return keyUnknown, b[i+1:] + } + } + + return -1, b +} + +// queue appends data to the end of ss.outBuf +func (ss *ServerShell) queue(data []byte) { + if len(ss.outBuf)+len(data) > cap(ss.outBuf) { + newOutBuf := make([]byte, len(ss.outBuf), 2*(len(ss.outBuf)+len(data))) + copy(newOutBuf, ss.outBuf) + ss.outBuf = newOutBuf + } + + oldLen := len(ss.outBuf) + ss.outBuf = ss.outBuf[:len(ss.outBuf)+len(data)] + copy(ss.outBuf[oldLen:], data) +} + +var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'} + +func isPrintable(key int) bool { + return key >= 32 && key < 127 +} + +// moveCursorToPos appends data to ss.outBuf which will move the cursor to the +// given, logical position in the text. +func (ss *ServerShell) moveCursorToPos(pos int) { + x := len(ss.prompt) + pos + y := x / ss.termWidth + x = x % ss.termWidth + + up := 0 + if y < ss.cursorY { + up = ss.cursorY - y + } + + down := 0 + if y > ss.cursorY { + down = y - ss.cursorY + } + + left := 0 + if x < ss.cursorX { + left = ss.cursorX - x + } + + right := 0 + if x > ss.cursorX { + right = x - ss.cursorX + } + + movement := make([]byte, 3*(up+down+left+right)) + m := movement + for i := 0; i < up; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'A' + m = m[3:] + } + for i := 0; i < down; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'B' + m = m[3:] + } + for i := 0; i < left; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'D' + m = m[3:] + } + for i := 0; i < right; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'C' + m = m[3:] + } + + ss.cursorX = x + ss.cursorY = y + ss.queue(movement) +} + +const maxLineLength = 4096 + +// handleKey processes the given key and, optionally, returns a line of text +// that the user has entered. +func (ss *ServerShell) handleKey(key int) (line string, ok bool) { + switch key { + case keyBackspace: + if ss.pos == 0 { + return + } + ss.pos-- + + copy(ss.line[ss.pos:], ss.line[1+ss.pos:]) + ss.line = ss.line[:len(ss.line)-1] + ss.writeLine(ss.line[ss.pos:]) + ss.moveCursorToPos(ss.pos) + ss.queue(eraseUnderCursor) + case keyAltLeft: + // move left by a word. + if ss.pos == 0 { + return + } + ss.pos-- + for ss.pos > 0 { + if ss.line[ss.pos] != ' ' { + break + } + ss.pos-- + } + for ss.pos > 0 { + if ss.line[ss.pos] == ' ' { + ss.pos++ + break + } + ss.pos-- + } + ss.moveCursorToPos(ss.pos) + case keyAltRight: + // move right by a word. + for ss.pos < len(ss.line) { + if ss.line[ss.pos] == ' ' { + break + } + ss.pos++ + } + for ss.pos < len(ss.line) { + if ss.line[ss.pos] != ' ' { + break + } + ss.pos++ + } + ss.moveCursorToPos(ss.pos) + case keyLeft: + if ss.pos == 0 { + return + } + ss.pos-- + ss.moveCursorToPos(ss.pos) + case keyRight: + if ss.pos == len(ss.line) { + return + } + ss.pos++ + ss.moveCursorToPos(ss.pos) + case keyEnter: + ss.moveCursorToPos(len(ss.line)) + ss.queue([]byte("\r\n")) + line = string(ss.line) + ok = true + ss.line = ss.line[:0] + ss.pos = 0 + ss.cursorX = 0 + ss.cursorY = 0 + ss.maxLine = 0 + default: + if !isPrintable(key) { + return + } + if len(ss.line) == maxLineLength { + return + } + if len(ss.line) == cap(ss.line) { + newLine := make([]byte, len(ss.line), 2*(1+len(ss.line))) + copy(newLine, ss.line) + ss.line = newLine + } + ss.line = ss.line[:len(ss.line)+1] + copy(ss.line[ss.pos+1:], ss.line[ss.pos:]) + ss.line[ss.pos] = byte(key) + ss.writeLine(ss.line[ss.pos:]) + ss.pos++ + ss.moveCursorToPos(ss.pos) + } + return +} + +func (ss *ServerShell) writeLine(line []byte) { + for len(line) != 0 { + if ss.cursorX == ss.termWidth { + ss.queue([]byte("\r\n")) + ss.cursorX = 0 + ss.cursorY++ + if ss.cursorY > ss.maxLine { + ss.maxLine = ss.cursorY + } + } + + remainingOnLine := ss.termWidth - ss.cursorX + todo := len(line) + if todo > remainingOnLine { + todo = remainingOnLine + } + ss.queue(line[:todo]) + ss.cursorX += todo + line = line[todo:] + } +} + +// parsePtyRequest parses the payload of the pty-req message and extracts the +// dimensions of the terminal. See RFC 4254, section 6.2. +func parsePtyRequest(s []byte) (width, height int, ok bool) { + _, s, ok = parseString(s) + if !ok { + return + } + width32, s, ok := parseUint32(s) + if !ok { + return + } + height32, _, ok := parseUint32(s) + width = int(width32) + height = int(height32) + if width < 1 { + ok = false + } + if height < 1 { + ok = false + } + return +} + +func (ss *ServerShell) Write(buf []byte) (n int, err os.Error) { + return ss.c.Write(buf) +} + +// ReadLine returns a line of input from the terminal. +func (ss *ServerShell) ReadLine() (line string, err os.Error) { + ss.writeLine([]byte(ss.prompt)) + ss.c.Write(ss.outBuf) + ss.outBuf = ss.outBuf[:0] + + for { + // ss.remainder is a slice at the beginning of ss.inBuf + // containing a partial key sequence + readBuf := ss.inBuf[len(ss.remainder):] + var n int + n, err = ss.c.Read(readBuf) + if err == nil { + ss.remainder = ss.inBuf[:n+len(ss.remainder)] + rest := ss.remainder + lineOk := false + for !lineOk { + var key int + key, rest = bytesToKey(rest) + if key < 0 { + break + } + if key == keyCtrlD { + return "", os.EOF + } + line, lineOk = ss.handleKey(key) + } + if len(rest) > 0 { + n := copy(ss.inBuf[:], rest) + ss.remainder = ss.inBuf[:n] + } else { + ss.remainder = nil + } + ss.c.Write(ss.outBuf) + ss.outBuf = ss.outBuf[:0] + if lineOk { + return + } + continue + } + + if req, ok := err.(ChannelRequest); ok { + ok := false + switch req.Request { + case "pty-req": + ss.termWidth, ss.termHeight, ok = parsePtyRequest(req.Payload) + if !ok { + ss.termWidth = 80 + ss.termHeight = 24 + } + case "shell": + ok = true + if len(req.Payload) > 0 { + // We don't accept any commands, only the default shell. + ok = false + } + case "env": + ok = true + } + if req.WantReply { + ss.c.AckRequest(ok) + } + } else { + return "", err + } + } + panic("unreachable") +} diff --git a/libgo/go/exp/ssh/server_shell_test.go b/libgo/go/exp/ssh/server_shell_test.go new file mode 100644 index 00000000000..622cf7cfada --- /dev/null +++ b/libgo/go/exp/ssh/server_shell_test.go @@ -0,0 +1,134 @@ +// 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 ssh + +import ( + "testing" + "os" +) + +type MockChannel struct { + toSend []byte + bytesPerRead int + received []byte +} + +func (c *MockChannel) Accept() os.Error { + return nil +} + +func (c *MockChannel) Reject(RejectionReason, string) os.Error { + return nil +} + +func (c *MockChannel) Read(data []byte) (n int, err os.Error) { + n = len(data) + if n == 0 { + return + } + if n > len(c.toSend) { + n = len(c.toSend) + } + if n == 0 { + return 0, os.EOF + } + if c.bytesPerRead > 0 && n > c.bytesPerRead { + n = c.bytesPerRead + } + copy(data, c.toSend[:n]) + c.toSend = c.toSend[n:] + return +} + +func (c *MockChannel) Write(data []byte) (n int, err os.Error) { + c.received = append(c.received, data...) + return len(data), nil +} + +func (c *MockChannel) Close() os.Error { + return nil +} + +func (c *MockChannel) AckRequest(ok bool) os.Error { + return nil +} + +func (c *MockChannel) ChannelType() string { + return "" +} + +func (c *MockChannel) ExtraData() []byte { + return nil +} + +func TestClose(t *testing.T) { + c := &MockChannel{} + ss := NewServerShell(c, "> ") + line, err := ss.ReadLine() + if line != "" { + t.Errorf("Expected empty line but got: %s", line) + } + if err != os.EOF { + t.Errorf("Error should have been EOF but got: %s", err) + } +} + +var keyPressTests = []struct { + in string + line string + err os.Error +}{ + { + "", + "", + os.EOF, + }, + { + "\r", + "", + nil, + }, + { + "foo\r", + "foo", + nil, + }, + { + "a\x1b[Cb\r", // right + "ab", + nil, + }, + { + "a\x1b[Db\r", // left + "ba", + nil, + }, + { + "a\177b\r", // backspace + "b", + nil, + }, +} + +func TestKeyPresses(t *testing.T) { + for i, test := range keyPressTests { + for j := 0; j < len(test.in); j++ { + c := &MockChannel{ + toSend: []byte(test.in), + bytesPerRead: j, + } + ss := NewServerShell(c, "> ") + line, err := ss.ReadLine() + if line != test.line { + t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line) + break + } + if err != test.err { + t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err) + break + } + } + } +} diff --git a/libgo/go/exp/ssh/transport.go b/libgo/go/exp/ssh/transport.go new file mode 100644 index 00000000000..5994004d866 --- /dev/null +++ b/libgo/go/exp/ssh/transport.go @@ -0,0 +1,369 @@ +// 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 ssh + +import ( + "bufio" + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/subtle" + "hash" + "io" + "net" + "os" + "sync" +) + +const ( + paddingMultiple = 16 // TODO(dfc) does this need to be configurable? +) + +// filteredConn reduces the set of methods exposed when embeddeding +// a net.Conn inside ssh.transport. +// TODO(dfc) suggestions for a better name will be warmly received. +type filteredConn interface { + // Close closes the connection. + Close() os.Error + + // LocalAddr returns the local network address. + LocalAddr() net.Addr + + // RemoteAddr returns the remote network address. + RemoteAddr() net.Addr +} + +// Types implementing packetWriter provide the ability to send packets to +// an SSH peer. +type packetWriter interface { + // Encrypt and send a packet of data to the remote peer. + writePacket(packet []byte) os.Error +} + +// transport represents the SSH connection to the remote peer. +type transport struct { + reader + writer + + filteredConn +} + +// reader represents the incoming connection state. +type reader struct { + io.Reader + common +} + +// writer represnts the outgoing connection state. +type writer struct { + *sync.Mutex // protects writer.Writer from concurrent writes + *bufio.Writer + paddingMultiple int + rand io.Reader + common +} + +// common represents the cipher state needed to process messages in a single +// direction. +type common struct { + seqNum uint32 + mac hash.Hash + cipher cipher.Stream + + cipherAlgo string + macAlgo string + compressionAlgo string +} + +// Read and decrypt a single packet from the remote peer. +func (r *reader) readOnePacket() ([]byte, os.Error) { + var lengthBytes = make([]byte, 5) + var macSize uint32 + + if _, err := io.ReadFull(r, lengthBytes); err != nil { + return nil, err + } + + if r.cipher != nil { + r.cipher.XORKeyStream(lengthBytes, lengthBytes) + } + + if r.mac != nil { + r.mac.Reset() + seqNumBytes := []byte{ + byte(r.seqNum >> 24), + byte(r.seqNum >> 16), + byte(r.seqNum >> 8), + byte(r.seqNum), + } + r.mac.Write(seqNumBytes) + r.mac.Write(lengthBytes) + macSize = uint32(r.mac.Size()) + } + + length := uint32(lengthBytes[0])<<24 | uint32(lengthBytes[1])<<16 | uint32(lengthBytes[2])<<8 | uint32(lengthBytes[3]) + paddingLength := uint32(lengthBytes[4]) + + if length <= paddingLength+1 { + return nil, os.NewError("invalid packet length") + } + if length > maxPacketSize { + return nil, os.NewError("packet too large") + } + + packet := make([]byte, length-1+macSize) + if _, err := io.ReadFull(r, packet); err != nil { + return nil, err + } + mac := packet[length-1:] + if r.cipher != nil { + r.cipher.XORKeyStream(packet, packet[:length-1]) + } + + if r.mac != nil { + r.mac.Write(packet[:length-1]) + if subtle.ConstantTimeCompare(r.mac.Sum(), mac) != 1 { + return nil, os.NewError("ssh: MAC failure") + } + } + + r.seqNum++ + return packet[:length-paddingLength-1], nil +} + +// Read and decrypt next packet discarding debug and noop messages. +func (t *transport) readPacket() ([]byte, os.Error) { + for { + packet, err := t.readOnePacket() + if err != nil { + return nil, err + } + if packet[0] != msgIgnore && packet[0] != msgDebug { + return packet, nil + } + } + panic("unreachable") +} + +// Encrypt and send a packet of data to the remote peer. +func (w *writer) writePacket(packet []byte) os.Error { + w.Mutex.Lock() + defer w.Mutex.Unlock() + + paddingLength := paddingMultiple - (5+len(packet))%paddingMultiple + if paddingLength < 4 { + paddingLength += paddingMultiple + } + + length := len(packet) + 1 + paddingLength + lengthBytes := []byte{ + byte(length >> 24), + byte(length >> 16), + byte(length >> 8), + byte(length), + byte(paddingLength), + } + padding := make([]byte, paddingLength) + _, err := io.ReadFull(w.rand, padding) + if err != nil { + return err + } + + if w.mac != nil { + w.mac.Reset() + seqNumBytes := []byte{ + byte(w.seqNum >> 24), + byte(w.seqNum >> 16), + byte(w.seqNum >> 8), + byte(w.seqNum), + } + w.mac.Write(seqNumBytes) + w.mac.Write(lengthBytes) + w.mac.Write(packet) + w.mac.Write(padding) + } + + // TODO(dfc) lengthBytes, packet and padding should be + // subslices of a single buffer + if w.cipher != nil { + w.cipher.XORKeyStream(lengthBytes, lengthBytes) + w.cipher.XORKeyStream(packet, packet) + w.cipher.XORKeyStream(padding, padding) + } + + if _, err := w.Write(lengthBytes); err != nil { + return err + } + if _, err := w.Write(packet); err != nil { + return err + } + if _, err := w.Write(padding); err != nil { + return err + } + + if w.mac != nil { + if _, err := w.Write(w.mac.Sum()); err != nil { + return err + } + } + + if err := w.Flush(); err != nil { + return err + } + w.seqNum++ + return err +} + +// Send a message to the remote peer +func (t *transport) sendMessage(typ uint8, msg interface{}) os.Error { + packet := marshal(typ, msg) + return t.writePacket(packet) +} + +func newTransport(conn net.Conn, rand io.Reader) *transport { + return &transport{ + reader: reader{ + Reader: bufio.NewReader(conn), + }, + writer: writer{ + Writer: bufio.NewWriter(conn), + rand: rand, + Mutex: new(sync.Mutex), + }, + filteredConn: conn, + } +} + +type direction struct { + ivTag []byte + keyTag []byte + macKeyTag []byte +} + +// TODO(dfc) can this be made a constant ? +var ( + serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} + clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} +) + +// setupKeys sets the cipher and MAC keys from K, H and sessionId, as +// described in RFC 4253, section 6.4. direction should either be serverKeys +// (to setup server->client keys) or clientKeys (for client->server keys). +func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) os.Error { + h := hashFunc.New() + + blockSize := 16 + keySize := 16 + macKeySize := 20 + + iv := make([]byte, blockSize) + key := make([]byte, keySize) + macKey := make([]byte, macKeySize) + generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h) + generateKeyMaterial(key, d.keyTag, K, H, sessionId, h) + generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h) + + c.mac = truncatingMAC{12, hmac.NewSHA1(macKey)} + aes, err := aes.NewCipher(key) + if err != nil { + return err + } + c.cipher = cipher.NewCTR(aes, iv) + return nil +} + +// generateKeyMaterial fills out with key material generated from tag, K, H +// and sessionId, as specified in RFC 4253, section 7.2. +func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) { + var digestsSoFar []byte + + for len(out) > 0 { + h.Reset() + h.Write(K) + h.Write(H) + + if len(digestsSoFar) == 0 { + h.Write(tag) + h.Write(sessionId) + } else { + h.Write(digestsSoFar) + } + + digest := h.Sum() + n := copy(out, digest) + out = out[n:] + if len(out) > 0 { + digestsSoFar = append(digestsSoFar, digest...) + } + } +} + +// truncatingMAC wraps around a hash.Hash and truncates the output digest to +// a given size. +type truncatingMAC struct { + length int + hmac hash.Hash +} + +func (t truncatingMAC) Write(data []byte) (int, os.Error) { + return t.hmac.Write(data) +} + +func (t truncatingMAC) Sum() []byte { + digest := t.hmac.Sum() + return digest[:t.length] +} + +func (t truncatingMAC) Reset() { + t.hmac.Reset() +} + +func (t truncatingMAC) Size() int { + return t.length +} + +// maxVersionStringBytes is the maximum number of bytes that we'll accept as a +// version string. In the event that the client is talking a different protocol +// we need to set a limit otherwise we will keep using more and more memory +// while searching for the end of the version handshake. +const maxVersionStringBytes = 1024 + +// Read version string as specified by RFC 4253, section 4.2. +func readVersion(r io.Reader) (versionString []byte, ok bool) { + versionString = make([]byte, 0, 64) + seenCR := false + + var buf [1]byte +forEachByte: + for len(versionString) < maxVersionStringBytes { + _, err := io.ReadFull(r, buf[:]) + if err != nil { + return + } + b := buf[0] + + if !seenCR { + if b == '\r' { + seenCR = true + } + } else { + if b == '\n' { + ok = true + break forEachByte + } else { + seenCR = false + } + } + versionString = append(versionString, b) + } + + if ok { + // We need to remove the CR from versionString + versionString = versionString[:len(versionString)-1] + } + + return +} diff --git a/libgo/go/exp/ssh/transport_test.go b/libgo/go/exp/ssh/transport_test.go new file mode 100644 index 00000000000..9a610a7803c --- /dev/null +++ b/libgo/go/exp/ssh/transport_test.go @@ -0,0 +1,37 @@ +// 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 ssh + +import ( + "bufio" + "bytes" + "testing" +) + +func TestReadVersion(t *testing.T) { + buf := []byte(serverVersion) + result, ok := readVersion(bufio.NewReader(bytes.NewBuffer(buf))) + if !ok { + t.Error("readVersion didn't read version correctly") + } + if !bytes.Equal(buf[:len(buf)-2], result) { + t.Error("version read did not match expected") + } +} + +func TestReadVersionTooLong(t *testing.T) { + buf := make([]byte, maxVersionStringBytes+1) + if _, ok := readVersion(bufio.NewReader(bytes.NewBuffer(buf))); ok { + t.Errorf("readVersion consumed %d bytes without error", len(buf)) + } +} + +func TestReadVersionWithoutCRLF(t *testing.T) { + buf := []byte(serverVersion) + buf = buf[:len(buf)-1] + if _, ok := readVersion(bufio.NewReader(bytes.NewBuffer(buf))); ok { + t.Error("readVersion did not notice \\n was missing") + } +} diff --git a/libgo/go/exp/template/html/attr.go b/libgo/go/exp/template/html/attr.go new file mode 100644 index 00000000000..6a36c7b7181 --- /dev/null +++ b/libgo/go/exp/template/html/attr.go @@ -0,0 +1,175 @@ +// 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 html + +import ( + "strings" +) + +// attrTypeMap[n] describes the value of the given attribute. +// If an attribute affects (or can mask) the encoding or interpretation of +// other content, or affects the contents, idempotency, or credentials of a +// network message, then the value in this map is contentTypeUnsafe. +// This map is derived from HTML5, specifically +// http://www.w3.org/TR/html5/Overview.html#attributes-1 +// as well as "%URI"-typed attributes from +// http://www.w3.org/TR/html4/index/attributes.html +var attrTypeMap = map[string]contentType{ + "accept": contentTypePlain, + "accept-charset": contentTypeUnsafe, + "action": contentTypeURL, + "alt": contentTypePlain, + "archive": contentTypeURL, + "async": contentTypeUnsafe, + "autocomplete": contentTypePlain, + "autofocus": contentTypePlain, + "autoplay": contentTypePlain, + "background": contentTypeURL, + "border": contentTypePlain, + "checked": contentTypePlain, + "cite": contentTypeURL, + "challenge": contentTypeUnsafe, + "charset": contentTypeUnsafe, + "class": contentTypePlain, + "classid": contentTypeURL, + "codebase": contentTypeURL, + "cols": contentTypePlain, + "colspan": contentTypePlain, + "content": contentTypeUnsafe, + "contenteditable": contentTypePlain, + "contextmenu": contentTypePlain, + "controls": contentTypePlain, + "coords": contentTypePlain, + "crossorigin": contentTypeUnsafe, + "data": contentTypeURL, + "datetime": contentTypePlain, + "default": contentTypePlain, + "defer": contentTypeUnsafe, + "dir": contentTypePlain, + "dirname": contentTypePlain, + "disabled": contentTypePlain, + "draggable": contentTypePlain, + "dropzone": contentTypePlain, + "enctype": contentTypeUnsafe, + "for": contentTypePlain, + "form": contentTypeUnsafe, + "formaction": contentTypeURL, + "formenctype": contentTypeUnsafe, + "formmethod": contentTypeUnsafe, + "formnovalidate": contentTypeUnsafe, + "formtarget": contentTypePlain, + "headers": contentTypePlain, + "height": contentTypePlain, + "hidden": contentTypePlain, + "high": contentTypePlain, + "href": contentTypeURL, + "hreflang": contentTypePlain, + "http-equiv": contentTypeUnsafe, + "icon": contentTypeURL, + "id": contentTypePlain, + "ismap": contentTypePlain, + "keytype": contentTypeUnsafe, + "kind": contentTypePlain, + "label": contentTypePlain, + "lang": contentTypePlain, + "language": contentTypeUnsafe, + "list": contentTypePlain, + "longdesc": contentTypeURL, + "loop": contentTypePlain, + "low": contentTypePlain, + "manifest": contentTypeURL, + "max": contentTypePlain, + "maxlength": contentTypePlain, + "media": contentTypePlain, + "mediagroup": contentTypePlain, + "method": contentTypeUnsafe, + "min": contentTypePlain, + "multiple": contentTypePlain, + "name": contentTypePlain, + "novalidate": contentTypeUnsafe, + // Skip handler names from + // http://www.w3.org/TR/html5/Overview.html#event-handlers-on-elements-document-objects-and-window-objects + // since we have special handling in attrType. + "open": contentTypePlain, + "optimum": contentTypePlain, + "pattern": contentTypeUnsafe, + "placeholder": contentTypePlain, + "poster": contentTypeURL, + "profile": contentTypeURL, + "preload": contentTypePlain, + "pubdate": contentTypePlain, + "radiogroup": contentTypePlain, + "readonly": contentTypePlain, + "rel": contentTypeUnsafe, + "required": contentTypePlain, + "reversed": contentTypePlain, + "rows": contentTypePlain, + "rowspan": contentTypePlain, + "sandbox": contentTypeUnsafe, + "spellcheck": contentTypePlain, + "scope": contentTypePlain, + "scoped": contentTypePlain, + "seamless": contentTypePlain, + "selected": contentTypePlain, + "shape": contentTypePlain, + "size": contentTypePlain, + "sizes": contentTypePlain, + "span": contentTypePlain, + "src": contentTypeURL, + "srcdoc": contentTypeHTML, + "srclang": contentTypePlain, + "start": contentTypePlain, + "step": contentTypePlain, + "style": contentTypeCSS, + "tabindex": contentTypePlain, + "target": contentTypePlain, + "title": contentTypePlain, + "type": contentTypeUnsafe, + "usemap": contentTypeURL, + "value": contentTypeUnsafe, + "width": contentTypePlain, + "wrap": contentTypePlain, + "xmlns": contentTypeURL, +} + +// attrType returns a conservative (upper-bound on authority) guess at the +// type of the named attribute. +func attrType(name string) contentType { + name = strings.ToLower(name) + if strings.HasPrefix(name, "data-") { + // Strip data- so that custom attribute heuristics below are + // widely applied. + // Treat data-action as URL below. + name = name[5:] + } else if colon := strings.IndexRune(name, ':'); colon != -1 { + if name[:colon] == "xmlns" { + return contentTypeURL + } + // Treat svg:href and xlink:href as href below. + name = name[colon+1:] + } + if t, ok := attrTypeMap[name]; ok { + return t + } + // Treat partial event handler names as script. + if strings.HasPrefix(name, "on") { + return contentTypeJS + } + + // Heuristics to prevent "javascript:..." injection in custom + // data attributes and custom attributes like g:tweetUrl. + // http://www.w3.org/TR/html5/elements.html#embedding-custom-non-visible-data-with-the-data-attributes: + // "Custom data attributes are intended to store custom data + // private to the page or application, for which there are no + // more appropriate attributes or elements." + // Developers seem to store URL content in data URLs that start + // or end with "URI" or "URL". + if strings.Contains(name, "src") || + strings.Contains(name, "uri") || + strings.Contains(name, "url") { + return contentTypeURL + } + return contentTypePlain +} diff --git a/libgo/go/exp/template/html/clone.go b/libgo/go/exp/template/html/clone.go new file mode 100644 index 00000000000..803a64de12f --- /dev/null +++ b/libgo/go/exp/template/html/clone.go @@ -0,0 +1,90 @@ +// 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 html + +import ( + "template/parse" +) + +// clone clones a template Node. +func clone(n parse.Node) parse.Node { + switch t := n.(type) { + case *parse.ActionNode: + return cloneAction(t) + case *parse.IfNode: + b := new(parse.IfNode) + copyBranch(&b.BranchNode, &t.BranchNode) + return b + case *parse.ListNode: + return cloneList(t) + case *parse.RangeNode: + b := new(parse.RangeNode) + copyBranch(&b.BranchNode, &t.BranchNode) + return b + case *parse.TemplateNode: + return cloneTemplate(t) + case *parse.TextNode: + return cloneText(t) + case *parse.WithNode: + b := new(parse.WithNode) + copyBranch(&b.BranchNode, &t.BranchNode) + return b + } + panic("cloning " + n.String() + " is unimplemented") +} + +// cloneAction returns a deep clone of n. +func cloneAction(n *parse.ActionNode) *parse.ActionNode { + // We use keyless fields because they won't compile if a field is added. + return &parse.ActionNode{n.NodeType, n.Line, clonePipe(n.Pipe)} +} + +// cloneList returns a deep clone of n. +func cloneList(n *parse.ListNode) *parse.ListNode { + if n == nil { + return nil + } + // We use keyless fields because they won't compile if a field is added. + c := parse.ListNode{n.NodeType, make([]parse.Node, len(n.Nodes))} + for i, child := range n.Nodes { + c.Nodes[i] = clone(child) + } + return &c +} + +// clonePipe returns a shallow clone of n. +// The escaper does not modify pipe descendants in place so there's no need to +// clone deeply. +func clonePipe(n *parse.PipeNode) *parse.PipeNode { + if n == nil { + return nil + } + // We use keyless fields because they won't compile if a field is added. + return &parse.PipeNode{n.NodeType, n.Line, n.Decl, n.Cmds} +} + +// cloneTemplate returns a deep clone of n. +func cloneTemplate(n *parse.TemplateNode) *parse.TemplateNode { + // We use keyless fields because they won't compile if a field is added. + return &parse.TemplateNode{n.NodeType, n.Line, n.Name, clonePipe(n.Pipe)} +} + +// cloneText clones the given node sharing its []byte. +func cloneText(n *parse.TextNode) *parse.TextNode { + // We use keyless fields because they won't compile if a field is added. + return &parse.TextNode{n.NodeType, n.Text} +} + +// copyBranch clones src into dst. +func copyBranch(dst, src *parse.BranchNode) { + // We use keyless fields because they won't compile if a field is added. + *dst = parse.BranchNode{ + src.NodeType, + src.Line, + clonePipe(src.Pipe), + cloneList(src.List), + cloneList(src.ElseList), + } +} diff --git a/libgo/go/exp/template/html/clone_test.go b/libgo/go/exp/template/html/clone_test.go new file mode 100644 index 00000000000..d91542529b9 --- /dev/null +++ b/libgo/go/exp/template/html/clone_test.go @@ -0,0 +1,90 @@ +// 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 html + +import ( + "bytes" + "template" + "template/parse" + "testing" +) + +func TestClone(t *testing.T) { + tests := []struct { + input, want, wantClone string + }{ + { + `Hello, {{if true}}{{"<World>"}}{{end}}!`, + "Hello, <World>!", + "Hello, <World>!", + }, + { + `Hello, {{if false}}{{.X}}{{else}}{{"<World>"}}{{end}}!`, + "Hello, <World>!", + "Hello, <World>!", + }, + { + `Hello, {{with "<World>"}}{{.}}{{end}}!`, + "Hello, <World>!", + "Hello, <World>!", + }, + { + `{{range .}}<p>{{.}}</p>{{end}}`, + "<p>foo</p><p><bar></p><p>baz</p>", + "<p>foo</p><p><bar></p><p>baz</p>", + }, + { + `Hello, {{"<World>" | html}}!`, + "Hello, <World>!", + "Hello, <World>!", + }, + { + `Hello{{if 1}}, World{{else}}{{template "d"}}{{end}}!`, + "Hello, World!", + "Hello, World!", + }, + } + + for _, test := range tests { + s := template.Must(template.New("s").Parse(test.input)) + d := template.New("d") + d.Tree = &parse.Tree{Name: d.Name(), Root: cloneList(s.Root)} + + if want, got := s.Root.String(), d.Root.String(); want != got { + t.Errorf("want %q, got %q", want, got) + } + + d, err := Escape(d) + if err != nil { + t.Errorf("%q: failed to escape: %s", test.input, err) + continue + } + + if want, got := "s", s.Name(); want != got { + t.Errorf("want %q, got %q", want, got) + continue + } + if want, got := "d", d.Name(); want != got { + t.Errorf("want %q, got %q", want, got) + continue + } + + data := []string{"foo", "<bar>", "baz"} + + // Make sure escaping d did not affect s. + var b bytes.Buffer + s.Execute(&b, data) + if got := b.String(); got != test.want { + t.Errorf("%q: want %q, got %q", test.input, test.want, got) + continue + } + + b.Reset() + d.Execute(&b, data) + if got := b.String(); got != test.wantClone { + t.Errorf("%q: want %q, got %q", test.input, test.wantClone, got) + } + } +} diff --git a/libgo/go/exp/template/html/content.go b/libgo/go/exp/template/html/content.go new file mode 100644 index 00000000000..dcaff8c15c6 --- /dev/null +++ b/libgo/go/exp/template/html/content.go @@ -0,0 +1,94 @@ +// 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 html + +import ( + "fmt" +) + +// Strings of content from a trusted source. +type ( + // CSS encapsulates known safe content that matches any of: + // (1) The CSS3 stylesheet production, such as `p { color: purple }`. + // (2) The CSS3 rule production, such as `a[href=~"https:"].foo#bar`. + // (3) CSS3 declaration productions, such as `color: red; margin: 2px`. + // (4) The CSS3 value production, such as `rgba(0, 0, 255, 127)`. + // See http://www.w3.org/TR/css3-syntax/#style + CSS string + + // HTML encapsulates a known safe HTML document fragment. + // It should not be used for HTML from a third-party, or HTML with + // unclosed tags or comments. The outputs of a sound HTML sanitizer + // and a template escaped by this package are fine for use with HTML. + HTML string + + // HTMLAttr encapsulates an HTML attribute from a trusted source, + // for example: ` dir="ltr"`. + HTMLAttr string + + // JS encapsulates a known safe EcmaScript5 Expression, or example, + // `(x + y * z())`. + // Template authors are responsible for ensuring that typed expressions + // do not break the intended precedence and that there is no + // statement/expression ambiguity as when passing an expression like + // "{ foo: bar() }\n['foo']()", which is both a valid Expression and a + // valid Program with a very different meaning. + JS string + + // JSStr encapsulates a sequence of characters meant to be embedded + // between quotes in a JavaScript expression. + // The string must match a series of StringCharacters: + // StringCharacter :: SourceCharacter but not `\` or LineTerminator + // | EscapeSequence + // Note that LineContinuations are not allowed. + // JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not. + JSStr string + + // URL encapsulates a known safe URL as defined in RFC 3896. + // A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()` + // from a trusted source should go in the page, but by default dynamic + // `javascript:` URLs are filtered out since they are a frequently + // exploited injection vector. + URL string +) + +type contentType uint8 + +const ( + contentTypePlain contentType = iota + contentTypeCSS + contentTypeHTML + contentTypeHTMLAttr + contentTypeJS + contentTypeJSStr + contentTypeURL + // contentTypeUnsafe is used in attr.go for values that affect how + // embedded content and network messages are formed, vetted, + // or interpreted; or which credentials network messages carry. + contentTypeUnsafe +) + +// stringify converts its arguments to a string and the type of the content. +func stringify(args ...interface{}) (string, contentType) { + if len(args) == 1 { + switch s := args[0].(type) { + case string: + return s, contentTypePlain + case CSS: + return string(s), contentTypeCSS + case HTML: + return string(s), contentTypeHTML + case HTMLAttr: + return string(s), contentTypeHTMLAttr + case JS: + return string(s), contentTypeJS + case JSStr: + return string(s), contentTypeJSStr + case URL: + return string(s), contentTypeURL + } + } + return fmt.Sprint(args...), contentTypePlain +} diff --git a/libgo/go/exp/template/html/content_test.go b/libgo/go/exp/template/html/content_test.go new file mode 100644 index 00000000000..033dee1747c --- /dev/null +++ b/libgo/go/exp/template/html/content_test.go @@ -0,0 +1,222 @@ +// 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 html + +import ( + "bytes" + "strings" + "template" + "testing" +) + +func TestTypedContent(t *testing.T) { + data := []interface{}{ + `<b> "foo%" O'Reilly &bar;`, + CSS(`a[href =~ "//example.com"]#foo`), + HTML(`Hello, <b>World</b> &tc!`), + HTMLAttr(` dir="ltr"`), + JS(`c && alert("Hello, World!");`), + JSStr(`Hello, World & O'Reilly\x21`), + URL(`greeting=H%69&addressee=(World)`), + } + + // For each content sensitive escaper, see how it does on + // each of the typed strings above. + tests := []struct { + // A template containing a single {{.}}. + input string + want []string + }{ + { + `<style>{{.}} { color: blue }</style>`, + []string{ + `ZgotmplZ`, + // Allowed but not escaped. + `a[href =~ "//example.com"]#foo`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + }, + }, + { + `<div style="{{.}}">`, + []string{ + `ZgotmplZ`, + // Allowed and HTML escaped. + `a[href =~ "//example.com"]#foo`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + }, + }, + { + `{{.}}`, + []string{ + `<b> "foo%" O'Reilly &bar;`, + `a[href =~ "//example.com"]#foo`, + // Not escaped. + `Hello, <b>World</b> &tc!`, + ` dir="ltr"`, + `c && alert("Hello, World!");`, + `Hello, World & O'Reilly\x21`, + `greeting=H%69&addressee=(World)`, + }, + }, + { + `<a{{.}}>`, + []string{ + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + // Allowed and HTML escaped. + ` dir="ltr"`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + }, + }, + { + `<a title={{.}}>`, + []string{ + `<b> "foo%" O'Reilly &bar;`, + `a[href =~ "//example.com"]#foo`, + // Tags stripped, spaces escaped, entity not re-escaped. + `Hello, World &tc!`, + ` dir="ltr"`, + `c && alert("Hello, World!");`, + `Hello, World & O'Reilly\x21`, + `greeting=H%69&addressee=(World)`, + }, + }, + { + `<a title='{{.}}'>`, + []string{ + `<b> "foo%" O'Reilly &bar;`, + `a[href =~ "//example.com"]#foo`, + // Tags stripped, entity not re-escaped. + `Hello, World &tc!`, + ` dir="ltr"`, + `c && alert("Hello, World!");`, + `Hello, World & O'Reilly\x21`, + `greeting=H%69&addressee=(World)`, + }, + }, + { + `<textarea>{{.}}</textarea>`, + []string{ + `<b> "foo%" O'Reilly &bar;`, + `a[href =~ "//example.com"]#foo`, + // Angle brackets escaped to prevent injection of close tags, entity not re-escaped. + `Hello, <b>World</b> &tc!`, + ` dir="ltr"`, + `c && alert("Hello, World!");`, + `Hello, World & O'Reilly\x21`, + `greeting=H%69&addressee=(World)`, + }, + }, + { + `<script>alert({{.}})</script>`, + []string{ + `"\u003cb\u003e \"foo%\" O'Reilly &bar;"`, + `"a[href =~ \"//example.com\"]#foo"`, + `"Hello, \u003cb\u003eWorld\u003c/b\u003e &tc!"`, + `" dir=\"ltr\""`, + // Not escaped. + `c && alert("Hello, World!");`, + // Escape sequence not over-escaped. + `"Hello, World & O'Reilly\x21"`, + `"greeting=H%69&addressee=(World)"`, + }, + }, + { + `<button onclick="alert({{.}})">`, + []string{ + `"\u003cb\u003e \"foo%\" O'Reilly &bar;"`, + `"a[href =~ \"//example.com\"]#foo"`, + `"Hello, \u003cb\u003eWorld\u003c/b\u003e &amp;tc!"`, + `" dir=\"ltr\""`, + // Not JS escaped but HTML escaped. + `c && alert("Hello, World!");`, + // Escape sequence not over-escaped. + `"Hello, World & O'Reilly\x21"`, + `"greeting=H%69&addressee=(World)"`, + }, + }, + { + `<script>alert("{{.}}")</script>`, + []string{ + `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`, + `a[href =~ \x22\/\/example.com\x22]#foo`, + `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`, + ` dir=\x22ltr\x22`, + `c \x26\x26 alert(\x22Hello, World!\x22);`, + // Escape sequence not over-escaped. + `Hello, World \x26 O\x27Reilly\x21`, + `greeting=H%69\x26addressee=(World)`, + }, + }, + { + `<button onclick='alert("{{.}}")'>`, + []string{ + `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`, + `a[href =~ \x22\/\/example.com\x22]#foo`, + `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`, + ` dir=\x22ltr\x22`, + `c \x26\x26 alert(\x22Hello, World!\x22);`, + // Escape sequence not over-escaped. + `Hello, World \x26 O\x27Reilly\x21`, + `greeting=H%69\x26addressee=(World)`, + }, + }, + { + `<a href="?q={{.}}">`, + []string{ + `%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`, + `a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`, + `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`, + `%20dir%3d%22ltr%22`, + `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`, + `Hello%2c%20World%20%26%20O%27Reilly%5cx21`, + // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done. + `greeting=H%69&addressee=%28World%29`, + }, + }, + { + `<style>body { background: url('?img={{.}}') }</style>`, + []string{ + `%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`, + `a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`, + `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`, + `%20dir%3d%22ltr%22`, + `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`, + `Hello%2c%20World%20%26%20O%27Reilly%5cx21`, + // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done. + `greeting=H%69&addressee=%28World%29`, + }, + }, + } + + for _, test := range tests { + tmpl := template.Must(Escape(template.Must(template.New("x").Parse(test.input)))) + pre := strings.Index(test.input, "{{.}}") + post := len(test.input) - (pre + 5) + var b bytes.Buffer + for i, x := range data { + b.Reset() + if err := tmpl.Execute(&b, x); err != nil { + t.Errorf("%q with %v: %s", test.input, x, err) + continue + } + if want, got := test.want[i], b.String()[pre:b.Len()-post]; want != got { + t.Errorf("%q with %v:\nwant\n\t%q,\ngot\n\t%q\n", test.input, x, want, got) + continue + } + } + } +} diff --git a/libgo/go/exp/template/html/context.go b/libgo/go/exp/template/html/context.go index 41100688343..c44df4debc1 100644 --- a/libgo/go/exp/template/html/context.go +++ b/libgo/go/exp/template/html/context.go @@ -16,56 +16,176 @@ import ( // http://www.w3.org/TR/html5/the-end.html#parsing-html-fragments // where the context element is null. type context struct { - state state - delim delim + state state + delim delim + urlPart urlPart + jsCtx jsCtx + attr attr + element element + err *Error } func (c context) String() string { - return fmt.Sprintf("context{state: %s, delim: %s", c.state, c.delim) + return fmt.Sprintf("{%v %v %v %v %v %v %v}", c.state, c.delim, c.urlPart, c.jsCtx, c.attr, c.element, c.err) } -// eq is true if the two contexts are identical field-wise. +// eq returns whether two contexts are equal. func (c context) eq(d context) bool { - return c.state == d.state && c.delim == d.delim + return c.state == d.state && + c.delim == d.delim && + c.urlPart == d.urlPart && + c.jsCtx == d.jsCtx && + c.attr == d.attr && + c.element == d.element && + c.err == d.err +} + +// mangle produces an identifier that includes a suffix that distinguishes it +// from template names mangled with different contexts. +func (c context) mangle(templateName string) string { + // The mangled name for the default context is the input templateName. + if c.state == stateText { + return templateName + } + s := templateName + "$htmltemplate_" + c.state.String() + if c.delim != 0 { + s += "_" + c.delim.String() + } + if c.urlPart != 0 { + s += "_" + c.urlPart.String() + } + if c.jsCtx != 0 { + s += "_" + c.jsCtx.String() + } + if c.attr != 0 { + s += "_" + c.attr.String() + } + if c.element != 0 { + s += "_" + c.element.String() + } + return s } // state describes a high-level HTML parser state. // -// It bounds the top of the element stack, and by extension the HTML -// insertion mode, but also contains state that does not correspond to -// anything in the HTML5 parsing algorithm because a single token -// production in the HTML grammar may contain embedded actions in a template. -// For instance, the quoted HTML attribute produced by +// It bounds the top of the element stack, and by extension the HTML insertion +// mode, but also contains state that does not correspond to anything in the +// HTML5 parsing algorithm because a single token production in the HTML +// grammar may contain embedded actions in a template. For instance, the quoted +// HTML attribute produced by // <div title="Hello {{.World}}"> // is a single token in HTML's grammar but in a template spans several nodes. type state uint8 const ( - // statePCDATA is parsed character data. An HTML parser is in + // stateText is parsed character data. An HTML parser is in // this state when its parse position is outside an HTML tag, // directive, comment, and special element body. - statePCDATA state = iota + stateText state = iota // stateTag occurs before an HTML attribute or the end of a tag. stateTag - // stateURI occurs inside an HTML attribute whose content is a URI. - stateURI + // stateAttrName occurs inside an attribute name. + // It occurs between the ^'s in ` ^name^ = value`. + stateAttrName + // stateAfterName occurs after an attr name has ended but before any + // equals sign. It occurs between the ^'s in ` name^ ^= value`. + stateAfterName + // stateBeforeValue occurs after the equals sign but before the value. + // It occurs between the ^'s in ` name =^ ^value`. + stateBeforeValue + // stateHTMLCmt occurs inside an <!-- HTML comment -->. + stateHTMLCmt + // stateRCDATA occurs inside an RCDATA element (<textarea> or <title>) + // as described at http://dev.w3.org/html5/spec/syntax.html#elements-0 + stateRCDATA + // stateAttr occurs inside an HTML attribute whose content is text. + stateAttr + // stateURL occurs inside an HTML attribute whose content is a URL. + stateURL + // stateJS occurs inside an event handler or script element. + stateJS + // stateJSDqStr occurs inside a JavaScript double quoted string. + stateJSDqStr + // stateJSSqStr occurs inside a JavaScript single quoted string. + stateJSSqStr + // stateJSRegexp occurs inside a JavaScript regexp literal. + stateJSRegexp + // stateJSBlockCmt occurs inside a JavaScript /* block comment */. + stateJSBlockCmt + // stateJSLineCmt occurs inside a JavaScript // line comment. + stateJSLineCmt + // stateCSS occurs inside a <style> element or style attribute. + stateCSS + // stateCSSDqStr occurs inside a CSS double quoted string. + stateCSSDqStr + // stateCSSSqStr occurs inside a CSS single quoted string. + stateCSSSqStr + // stateCSSDqURL occurs inside a CSS double quoted url("..."). + stateCSSDqURL + // stateCSSSqURL occurs inside a CSS single quoted url('...'). + stateCSSSqURL + // stateCSSURL occurs inside a CSS unquoted url(...). + stateCSSURL + // stateCSSBlockCmt occurs inside a CSS /* block comment */. + stateCSSBlockCmt + // stateCSSLineCmt occurs inside a CSS // line comment. + stateCSSLineCmt // stateError is an infectious error state outside any valid // HTML/CSS/JS construct. stateError ) var stateNames = [...]string{ - statePCDATA: "statePCDATA", - stateTag: "stateTag", - stateURI: "stateURI", - stateError: "stateError", + stateText: "stateText", + stateTag: "stateTag", + stateAttrName: "stateAttrName", + stateAfterName: "stateAfterName", + stateBeforeValue: "stateBeforeValue", + stateHTMLCmt: "stateHTMLCmt", + stateRCDATA: "stateRCDATA", + stateAttr: "stateAttr", + stateURL: "stateURL", + stateJS: "stateJS", + stateJSDqStr: "stateJSDqStr", + stateJSSqStr: "stateJSSqStr", + stateJSRegexp: "stateJSRegexp", + stateJSBlockCmt: "stateJSBlockCmt", + stateJSLineCmt: "stateJSLineCmt", + stateCSS: "stateCSS", + stateCSSDqStr: "stateCSSDqStr", + stateCSSSqStr: "stateCSSSqStr", + stateCSSDqURL: "stateCSSDqURL", + stateCSSSqURL: "stateCSSSqURL", + stateCSSURL: "stateCSSURL", + stateCSSBlockCmt: "stateCSSBlockCmt", + stateCSSLineCmt: "stateCSSLineCmt", + stateError: "stateError", } func (s state) String() string { - if uint(s) < uint(len(stateNames)) { + if int(s) < len(stateNames) { return stateNames[s] } - return fmt.Sprintf("illegal state %d", uint(s)) + return fmt.Sprintf("illegal state %d", int(s)) +} + +// isComment is true for any state that contains content meant for template +// authors & maintainers, not for end-users or machines. +func isComment(s state) bool { + switch s { + case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt: + return true + } + return false +} + +// isInTag return whether s occurs solely inside an HTML tag. +func isInTag(s state) bool { + switch s { + case stateTag, stateAttrName, stateAfterName, stateBeforeValue, stateAttr: + return true + } + return false } // delim is the delimiter that will end the current HTML attribute. @@ -91,8 +211,129 @@ var delimNames = [...]string{ } func (d delim) String() string { - if uint(d) < uint(len(delimNames)) { + if int(d) < len(delimNames) { return delimNames[d] } - return fmt.Sprintf("illegal delim %d", uint(d)) + return fmt.Sprintf("illegal delim %d", int(d)) +} + +// urlPart identifies a part in an RFC 3986 hierarchical URL to allow different +// encoding strategies. +type urlPart uint8 + +const ( + // urlPartNone occurs when not in a URL, or possibly at the start: + // ^ in "^http://auth/path?k=v#frag". + urlPartNone urlPart = iota + // urlPartPreQuery occurs in the scheme, authority, or path; between the + // ^s in "h^ttp://auth/path^?k=v#frag". + urlPartPreQuery + // urlPartQueryOrFrag occurs in the query portion between the ^s in + // "http://auth/path?^k=v#frag^". + urlPartQueryOrFrag + // urlPartUnknown occurs due to joining of contexts both before and + // after the query separator. + urlPartUnknown +) + +var urlPartNames = [...]string{ + urlPartNone: "urlPartNone", + urlPartPreQuery: "urlPartPreQuery", + urlPartQueryOrFrag: "urlPartQueryOrFrag", + urlPartUnknown: "urlPartUnknown", +} + +func (u urlPart) String() string { + if int(u) < len(urlPartNames) { + return urlPartNames[u] + } + return fmt.Sprintf("illegal urlPart %d", int(u)) +} + +// jsCtx determines whether a '/' starts a regular expression literal or a +// division operator. +type jsCtx uint8 + +const ( + // jsCtxRegexp occurs where a '/' would start a regexp literal. + jsCtxRegexp jsCtx = iota + // jsCtxDivOp occurs where a '/' would start a division operator. + jsCtxDivOp + // jsCtxUnknown occurs where a '/' is ambiguous due to context joining. + jsCtxUnknown +) + +func (c jsCtx) String() string { + switch c { + case jsCtxRegexp: + return "jsCtxRegexp" + case jsCtxDivOp: + return "jsCtxDivOp" + case jsCtxUnknown: + return "jsCtxUnknown" + } + return fmt.Sprintf("illegal jsCtx %d", int(c)) +} + +// element identifies the HTML element when inside a start tag or special body. +// Certain HTML element (for example <script> and <style>) have bodies that are +// treated differently from stateText so the element type is necessary to +// transition into the correct context at the end of a tag and to identify the +// end delimiter for the body. +type element uint8 + +const ( + // elementNone occurs outside a special tag or special element body. + elementNone element = iota + // elementScript corresponds to the raw text <script> element. + elementScript + // elementStyle corresponds to the raw text <style> element. + elementStyle + // elementTextarea corresponds to the RCDATA <textarea> element. + elementTextarea + // elementTitle corresponds to the RCDATA <title> element. + elementTitle +) + +var elementNames = [...]string{ + elementNone: "elementNone", + elementScript: "elementScript", + elementStyle: "elementStyle", + elementTextarea: "elementTextarea", + elementTitle: "elementTitle", +} + +func (e element) String() string { + if int(e) < len(elementNames) { + return elementNames[e] + } + return fmt.Sprintf("illegal element %d", int(e)) +} + +// attr identifies the most recent HTML attribute when inside a start tag. +type attr uint8 + +const ( + // attrNone corresponds to a normal attribute or no attribute. + attrNone attr = iota + // attrScript corresponds to an event handler attribute. + attrScript + // attrStyle corresponds to the style attribute whose value is CSS. + attrStyle + // attrURL corresponds to an attribute whose value is a URL. + attrURL +) + +var attrNames = [...]string{ + attrNone: "attrNone", + attrScript: "attrScript", + attrStyle: "attrStyle", + attrURL: "attrURL", +} + +func (a attr) String() string { + if int(a) < len(attrNames) { + return attrNames[a] + } + return fmt.Sprintf("illegal attr %d", int(a)) } diff --git a/libgo/go/exp/template/html/css.go b/libgo/go/exp/template/html/css.go new file mode 100644 index 00000000000..c22ec6df0d0 --- /dev/null +++ b/libgo/go/exp/template/html/css.go @@ -0,0 +1,268 @@ +// 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 html + +import ( + "bytes" + "fmt" + "unicode" + "utf8" +) + +// endsWithCSSKeyword returns whether b ends with an ident that +// case-insensitively matches the lower-case kw. +func endsWithCSSKeyword(b []byte, kw string) bool { + i := len(b) - len(kw) + if i < 0 { + // Too short. + return false + } + if i != 0 { + r, _ := utf8.DecodeLastRune(b[:i]) + if isCSSNmchar(r) { + // Too long. + return false + } + } + // Many CSS keywords, such as "!important" can have characters encoded, + // but the URI production does not allow that according to + // http://www.w3.org/TR/css3-syntax/#TOK-URI + // This does not attempt to recognize encoded keywords. For example, + // given "\75\72\6c" and "url" this return false. + return string(bytes.ToLower(b[i:])) == kw +} + +// isCSSNmchar returns whether rune is allowed anywhere in a CSS identifier. +func isCSSNmchar(rune int) bool { + // Based on the CSS3 nmchar production but ignores multi-rune escape + // sequences. + // http://www.w3.org/TR/css3-syntax/#SUBTOK-nmchar + return 'a' <= rune && rune <= 'z' || + 'A' <= rune && rune <= 'Z' || + '0' <= rune && rune <= '9' || + '-' == rune || + '_' == rune || + // Non-ASCII cases below. + 0x80 <= rune && rune <= 0xd7ff || + 0xe000 <= rune && rune <= 0xfffd || + 0x10000 <= rune && rune <= 0x10ffff +} + +// decodeCSS decodes CSS3 escapes given a sequence of stringchars. +// If there is no change, it returns the input, otherwise it returns a slice +// backed by a new array. +// http://www.w3.org/TR/css3-syntax/#SUBTOK-stringchar defines stringchar. +func decodeCSS(s []byte) []byte { + i := bytes.IndexByte(s, '\\') + if i == -1 { + return s + } + // The UTF-8 sequence for a codepoint is never longer than 1 + the + // number hex digits need to represent that codepoint, so len(s) is an + // upper bound on the output length. + b := make([]byte, 0, len(s)) + for len(s) != 0 { + i := bytes.IndexByte(s, '\\') + if i == -1 { + i = len(s) + } + b, s = append(b, s[:i]...), s[i:] + if len(s) < 2 { + break + } + // http://www.w3.org/TR/css3-syntax/#SUBTOK-escape + // escape ::= unicode | '\' [#x20-#x7E#x80-#xD7FF#xE000-#xFFFD#x10000-#x10FFFF] + if isHex(s[1]) { + // http://www.w3.org/TR/css3-syntax/#SUBTOK-unicode + // unicode ::= '\' [0-9a-fA-F]{1,6} wc? + j := 2 + for j < len(s) && j < 7 && isHex(s[j]) { + j++ + } + rune := hexDecode(s[1:j]) + if rune > unicode.MaxRune { + rune, j = rune/16, j-1 + } + n := utf8.EncodeRune(b[len(b):cap(b)], rune) + // The optional space at the end allows a hex + // sequence to be followed by a literal hex. + // string(decodeCSS([]byte(`\A B`))) == "\nB" + b, s = b[:len(b)+n], skipCSSSpace(s[j:]) + } else { + // `\\` decodes to `\` and `\"` to `"`. + _, n := utf8.DecodeRune(s[1:]) + b, s = append(b, s[1:1+n]...), s[1+n:] + } + } + return b +} + +// isHex returns whether the given character is a hex digit. +func isHex(c byte) bool { + return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' +} + +// hexDecode decodes a short hex digit sequence: "10" -> 16. +func hexDecode(s []byte) int { + n := 0 + for _, c := range s { + n <<= 4 + switch { + case '0' <= c && c <= '9': + n |= int(c - '0') + case 'a' <= c && c <= 'f': + n |= int(c-'a') + 10 + case 'A' <= c && c <= 'F': + n |= int(c-'A') + 10 + default: + panic(fmt.Sprintf("Bad hex digit in %q", s)) + } + } + return n +} + +// skipCSSSpace returns a suffix of c, skipping over a single space. +func skipCSSSpace(c []byte) []byte { + if len(c) == 0 { + return c + } + // wc ::= #x9 | #xA | #xC | #xD | #x20 + switch c[0] { + case '\t', '\n', '\f', ' ': + return c[1:] + case '\r': + // This differs from CSS3's wc production because it contains a + // probable spec error whereby wc contains all the single byte + // sequences in nl (newline) but not CRLF. + if len(c) >= 2 && c[1] == '\n' { + return c[2:] + } + return c[1:] + } + return c +} + +// isCSSSpace returns whether b is a CSS space char as defined in wc. +func isCSSSpace(b byte) bool { + switch b { + case '\t', '\n', '\f', '\r', ' ': + return true + } + return false +} + +// cssEscaper escapes HTML and CSS special characters using \<hex>+ escapes. +func cssEscaper(args ...interface{}) string { + s, _ := stringify(args...) + var b bytes.Buffer + written := 0 + for i, r := range s { + var repl string + switch r { + case 0: + repl = `\0` + case '\t': + repl = `\9` + case '\n': + repl = `\a` + case '\f': + repl = `\c` + case '\r': + repl = `\d` + // Encode HTML specials as hex so the output can be embedded + // in HTML attributes without further encoding. + case '"': + repl = `\22` + case '&': + repl = `\26` + case '\'': + repl = `\27` + case '(': + repl = `\28` + case ')': + repl = `\29` + case '+': + repl = `\2b` + case '/': + repl = `\2f` + case ':': + repl = `\3a` + case ';': + repl = `\3b` + case '<': + repl = `\3c` + case '>': + repl = `\3e` + case '\\': + repl = `\\` + case '{': + repl = `\7b` + case '}': + repl = `\7d` + default: + continue + } + b.WriteString(s[written:i]) + b.WriteString(repl) + written = i + utf8.RuneLen(r) + if repl != `\\` && (written == len(s) || isHex(s[written]) || isCSSSpace(s[written])) { + b.WriteByte(' ') + } + } + if written == 0 { + return s + } + b.WriteString(s[written:]) + return b.String() +} + +var expressionBytes = []byte("expression") +var mozBindingBytes = []byte("mozbinding") + +// cssValueFilter allows innocuous CSS values in the output including CSS +// quantities (10px or 25%), ID or class literals (#foo, .bar), keyword values +// (inherit, blue), and colors (#888). +// It filters out unsafe values, such as those that affect token boundaries, +// and anything that might execute scripts. +func cssValueFilter(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeCSS { + return s + } + b, id := decodeCSS([]byte(s)), make([]byte, 0, 64) + + // CSS3 error handling is specified as honoring string boundaries per + // http://www.w3.org/TR/css3-syntax/#error-handling : + // Malformed declarations. User agents must handle unexpected + // tokens encountered while parsing a declaration by reading until + // the end of the declaration, while observing the rules for + // matching pairs of (), [], {}, "", and '', and correctly handling + // escapes. For example, a malformed declaration may be missing a + // property, colon (:) or value. + // So we need to make sure that values do not have mismatched bracket + // or quote characters to prevent the browser from restarting parsing + // inside a string that might embed JavaScript source. + for i, c := range b { + switch c { + case 0, '"', '\'', '(', ')', '/', ';', '@', '[', '\\', ']', '`', '{', '}': + return filterFailsafe + case '-': + // Disallow <!-- or -->. + // -- should not appear in valid identifiers. + if i != 0 && '-' == b[i-1] { + return filterFailsafe + } + default: + if c < 0x80 && isCSSNmchar(int(c)) { + id = append(id, c) + } + } + } + id = bytes.ToLower(id) + if bytes.Index(id, expressionBytes) != -1 || bytes.Index(id, mozBindingBytes) != -1 { + return filterFailsafe + } + return string(b) +} diff --git a/libgo/go/exp/template/html/css_test.go b/libgo/go/exp/template/html/css_test.go new file mode 100644 index 00000000000..5f633e89442 --- /dev/null +++ b/libgo/go/exp/template/html/css_test.go @@ -0,0 +1,281 @@ +// 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 html + +import ( + "strconv" + "strings" + "testing" +) + +func TestEndsWithCSSKeyword(t *testing.T) { + tests := []struct { + css, kw string + want bool + }{ + {"", "url", false}, + {"url", "url", true}, + {"URL", "url", true}, + {"Url", "url", true}, + {"url", "important", false}, + {"important", "important", true}, + {"image-url", "url", false}, + {"imageurl", "url", false}, + {"image url", "url", true}, + } + for _, test := range tests { + got := endsWithCSSKeyword([]byte(test.css), test.kw) + if got != test.want { + t.Errorf("want %t but got %t for css=%v, kw=%v", test.want, got, test.css, test.kw) + } + } +} + +func TestIsCSSNmchar(t *testing.T) { + tests := []struct { + rune int + want bool + }{ + {0, false}, + {'0', true}, + {'9', true}, + {'A', true}, + {'Z', true}, + {'a', true}, + {'z', true}, + {'_', true}, + {'-', true}, + {':', false}, + {';', false}, + {' ', false}, + {0x7f, false}, + {0x80, true}, + {0x1234, true}, + {0xd800, false}, + {0xdc00, false}, + {0xfffe, false}, + {0x10000, true}, + {0x110000, false}, + } + for _, test := range tests { + got := isCSSNmchar(test.rune) + if got != test.want { + t.Errorf("%q: want %t but got %t", string(test.rune), test.want, got) + } + } +} + +func TestDecodeCSS(t *testing.T) { + tests := []struct { + css, want string + }{ + {``, ``}, + {`foo`, `foo`}, + {`foo\`, `foo`}, + {`foo\\`, `foo\`}, + {`\`, ``}, + {`\A`, "\n"}, + {`\a`, "\n"}, + {`\0a`, "\n"}, + {`\00000a`, "\n"}, + {`\000000a`, "\u0000a"}, + {`\1234 5`, "\u1234" + "5"}, + {`\1234\20 5`, "\u1234" + " 5"}, + {`\1234\A 5`, "\u1234" + "\n5"}, + {"\\1234\t5", "\u1234" + "5"}, + {"\\1234\n5", "\u1234" + "5"}, + {"\\1234\r\n5", "\u1234" + "5"}, + {`\12345`, "\U00012345"}, + {`\\`, `\`}, + {`\\ `, `\ `}, + {`\"`, `"`}, + {`\'`, `'`}, + {`\.`, `.`}, + {`\. .`, `. .`}, + { + `The \3c i\3equick\3c/i\3e,\d\A\3cspan style=\27 color:brown\27\3e brown\3c/span\3e fox jumps\2028over the \3c canine class=\22lazy\22 \3e dog\3c/canine\3e`, + "The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>", + }, + } + for _, test := range tests { + got1 := string(decodeCSS([]byte(test.css))) + if got1 != test.want { + t.Errorf("%q: want\n\t%q\nbut got\n\t%q", test.css, test.want, got1) + } + recoded := cssEscaper(got1) + if got2 := string(decodeCSS([]byte(recoded))); got2 != test.want { + t.Errorf("%q: escape & decode not dual for %q", test.css, recoded) + } + } +} + +func TestHexDecode(t *testing.T) { + for i := 0; i < 0x200000; i += 101 /* coprime with 16 */ { + s := strconv.Itob(i, 16) + if got := hexDecode([]byte(s)); got != i { + t.Errorf("%s: want %d but got %d", s, i, got) + } + s = strings.ToUpper(s) + if got := hexDecode([]byte(s)); got != i { + t.Errorf("%s: want %d but got %d", s, i, got) + } + } +} + +func TestSkipCSSSpace(t *testing.T) { + tests := []struct { + css, want string + }{ + {"", ""}, + {"foo", "foo"}, + {"\n", ""}, + {"\r\n", ""}, + {"\r", ""}, + {"\t", ""}, + {" ", ""}, + {"\f", ""}, + {" foo", "foo"}, + {" foo", " foo"}, + {`\20`, `\20`}, + } + for _, test := range tests { + got := string(skipCSSSpace([]byte(test.css))) + if got != test.want { + t.Errorf("%q: want %q but got %q", test.css, test.want, got) + } + } +} + +func TestCSSEscaper(t *testing.T) { + input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" + + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !"#$%&'()*+,-./` + + `0123456789:;<=>?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\]^_` + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\x7f" + + "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") + + want := ("\\0\x01\x02\x03\x04\x05\x06\x07" + + "\x08\\9 \\a\x0b\\c \\d\x0E\x0F" + + "\x10\x11\x12\x13\x14\x15\x16\x17" + + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !\22#$%\26\27\28\29*\2b,-.\2f ` + + `0123456789\3a\3b\3c=\3e?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\\]^_` + + "`abcdefghijklmno" + + `pqrstuvwxyz\7b|\7d~` + "\u007f" + + "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") + + got := cssEscaper(input) + if got != want { + t.Errorf("encode: want\n\t%q\nbut got\n\t%q", want, got) + } + + got = string(decodeCSS([]byte(got))) + if input != got { + t.Errorf("decode: want\n\t%q\nbut got\n\t%q", input, got) + } +} + +func TestCSSValueFilter(t *testing.T) { + tests := []struct { + css, want string + }{ + {"", ""}, + {"foo", "foo"}, + {"0", "0"}, + {"0px", "0px"}, + {"-5px", "-5px"}, + {"1.25in", "1.25in"}, + {"+.33em", "+.33em"}, + {"100%", "100%"}, + {"12.5%", "12.5%"}, + {".foo", ".foo"}, + {"#bar", "#bar"}, + {"corner-radius", "corner-radius"}, + {"-moz-corner-radius", "-moz-corner-radius"}, + {"#000", "#000"}, + {"#48f", "#48f"}, + {"#123456", "#123456"}, + {"U+00-FF, U+980-9FF", "U+00-FF, U+980-9FF"}, + {"color: red", "color: red"}, + {"<!--", "ZgotmplZ"}, + {"-->", "ZgotmplZ"}, + {"<![CDATA[", "ZgotmplZ"}, + {"]]>", "ZgotmplZ"}, + {"</style", "ZgotmplZ"}, + {`"`, "ZgotmplZ"}, + {`'`, "ZgotmplZ"}, + {"`", "ZgotmplZ"}, + {"\x00", "ZgotmplZ"}, + {"/* foo */", "ZgotmplZ"}, + {"//", "ZgotmplZ"}, + {"[href=~", "ZgotmplZ"}, + {"expression(alert(1337))", "ZgotmplZ"}, + {"-expression(alert(1337))", "ZgotmplZ"}, + {"expression", "ZgotmplZ"}, + {"Expression", "ZgotmplZ"}, + {"EXPRESSION", "ZgotmplZ"}, + {"-moz-binding", "ZgotmplZ"}, + {"-expr\x00ession(alert(1337))", "ZgotmplZ"}, + {`-expr\0ession(alert(1337))`, "ZgotmplZ"}, + {`-express\69on(alert(1337))`, "ZgotmplZ"}, + {`-express\69 on(alert(1337))`, "ZgotmplZ"}, + {`-exp\72 ession(alert(1337))`, "ZgotmplZ"}, + {`-exp\52 ession(alert(1337))`, "ZgotmplZ"}, + {`-exp\000052 ession(alert(1337))`, "ZgotmplZ"}, + {`-expre\0000073sion`, "-expre\x073sion"}, + {`@import url evil.css`, "ZgotmplZ"}, + } + for _, test := range tests { + got := cssValueFilter(test.css) + if got != test.want { + t.Errorf("%q: want %q but got %q", test.css, test.want, got) + } + } +} + +func BenchmarkCSSEscaper(b *testing.B) { + for i := 0; i < b.N; i++ { + cssEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>") + } +} + +func BenchmarkCSSEscaperNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + cssEscaper("The quick, brown fox jumps over the lazy dog.") + } +} + +func BenchmarkDecodeCSS(b *testing.B) { + s := []byte(`The \3c i\3equick\3c/i\3e,\d\A\3cspan style=\27 color:brown\27\3e brown\3c/span\3e fox jumps\2028over the \3c canine class=\22lazy\22 \3edog\3c/canine\3e`) + b.ResetTimer() + for i := 0; i < b.N; i++ { + decodeCSS(s) + } +} + +func BenchmarkDecodeCSSNoSpecials(b *testing.B) { + s := []byte("The quick, brown fox jumps over the lazy dog.") + b.ResetTimer() + for i := 0; i < b.N; i++ { + decodeCSS(s) + } +} + +func BenchmarkCSSValueFilter(b *testing.B) { + for i := 0; i < b.N; i++ { + cssValueFilter(` e\78preS\0Sio/**/n(alert(1337))`) + } +} + +func BenchmarkCSSValueFilterOk(b *testing.B) { + for i := 0; i < b.N; i++ { + cssValueFilter(`Times New Roman`) + } +} diff --git a/libgo/go/exp/template/html/doc.go b/libgo/go/exp/template/html/doc.go new file mode 100644 index 00000000000..a9b78ca5157 --- /dev/null +++ b/libgo/go/exp/template/html/doc.go @@ -0,0 +1,190 @@ +// 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 html is a specialization of package template that automates the +construction of HTML output that is safe against code injection. + + +Introduction + +To use this package, invoke the standard template package to parse a template +set, and then use this package’s EscapeSet function to secure the set. +The arguments to EscapeSet are the template set and the names of all templates +that will be passed to Execute. + + set, err := new(template.Set).Parse(...) + set, err = EscapeSet(set, "templateName0", ...) + +If successful, set will now be injection-safe. Otherwise, the returned set will +be nil and an error, described below, will explain the problem. + +The template names do not need to include helper templates but should include +all names x used thus: + + set.Execute(out, x, ...) + +EscapeSet modifies the named templates in place to treat data values as plain +text safe for embedding in an HTML document. The escaping is contextual, so +actions can appear within JavaScript, CSS, and URI contexts without introducing'hazards. + +The security model used by this package assumes that template authors are +trusted, while Execute's data parameter is not. More details are provided below. + +Example + + tmpls, err := new(template.Set).Parse(`{{define "t'}}Hello, {{.}}!{{end}}`) + +when used by itself + + tmpls.Execute(out, "t", "<script>alert('you have been pwned')</script>") + +produces + + Hello, <script>alert('you have been pwned')</script>! + +but after securing with EscapeSet like this, + + tmpls, err := EscapeSet(tmpls, "t") + tmpls.Execute(out, "t", ...) + +produces the safe, escaped HTML output + + Hello, <script>alert('you have been pwned')</script>! + + +Contexts + +EscapeSet understands HTML, CSS, JavaScript, and URIs. It adds sanitizing +functions to each simple action pipeline, so given the excerpt + + <a href="/search?q={{.}}">{{.}}</a> + +EscapeSet will rewrite each {{.}} to add escaping functions where necessary, +in this case, + + <a href="/search?q={{. | urlquery}}">{{. | html}}</a> + + +Errors + +See the documentation of ErrorCode for details. + + +A fuller picture + +The rest of this package comment may be skipped on first reading; it includes +details necessary to understand escaping contexts and error messages. Most users +will not need to understand these details. + + +Contexts + +Assuming {{.}} is `O'Reilly: How are <i>you</i>?`, the table below shows +how {{.}} appears when used in the context to the left. + +Context {{.}} After +{{.}} O'Reilly: How are <i>you</i>? +<a title='{{.}}'> O'Reilly: How are you? +<a href="/{{.}}"> O'Reilly: How are %3ci%3eyou%3c/i%3e? +<a href="?q={{.}}"> O'Reilly%3a%20How%20are%3ci%3e...%3f +<a onx='f("{{.}}")'> O\x27Reilly: How are \x3ci\x3eyou...? +<a onx='f({{.}})'> "O\x27Reilly: How are \x3ci\x3eyou...?" +<a onx='pattern = /{{.}}/;'> O\x27Reilly: How are \x3ci\x3eyou...\x3f + +If used in an unsafe context, then the value might be filtered out: + +Context {{.}} After +<a href="{{.}}"> #ZgotmplZ + +since "O'Reilly:" is not an allowed protocol like "http:". + + +If {{.}} is the innocuous word, `left`, then it can appear more widely, + +Context {{.}} After +{{.}} left +<a title='{{.}}'> left +<a href='{{.}}'> left +<a href='/{{.}}'> left +<a href='?dir={{.}}'> left +<a style="border-{{.}}: 4px"> left +<a style="align: {{.}}"> left +<a style="background: '{{.}}'> left +<a style="background: url('{{.}}')> left +<style>p.{{.}} {color:red}</style> left + +Non-string values can be used in JavaScript contexts. +If {{.}} is + + []struct{A,B string}{ "foo", "bar" } + +in the escaped template + + <script>var pair = {{.}};</script> + +then the template output is + + <script>var pair = {"A": "foo", "B": "bar"};</script> + +See package json to understand how non-string content is marshalled for +embedding in JavaScript contexts. + + +Typed Strings + +By default, EscapeSet assumes all pipelines produce a plain text string. It +adds escaping pipeline stages necessary to correctly and safely embed that +plain text string in the appropriate context. + +When a data value is not plain text, you can make sure it is not over-escaped +by marking it with its type. + +Types HTML, JS, URL, and others from content.go can carry safe content that is +exempted from escaping. + +The template + + Hello, {{.}}! + +can be invoked with + + tmpl.Execute(out, HTML(`<b>World</b>`)) + +to produce + + Hello, <b>World</b>! + +instead of the + + Hello, <b>World<b>! + +that would have been produced if {{.}} was a regular string. + + +Security Model + +http://js-quasis-libraries-and-repl.googlecode.com/svn/trunk/safetemplate.html#problem_definition defines "safe" as used by this package. + +This package assumes that template authors are trusted, that Execute's data +parameter is not, and seeks to preserve the properties below in the face +of untrusted data: + +Structure Preservation Property +"... when a template author writes an HTML tag in a safe templating language, +the browser will interpret the corresponding portion of the output as a tag +regardless of the values of untrusted data, and similarly for other structures +such as attribute boundaries and JS and CSS string boundaries." + +Code Effect Property +"... only code specified by the template author should run as a result of +injecting the template output into a page and all code specified by the +template author should run as a result of the same." + +Least Surprise Property +"A developer (or code reviewer) familiar with HTML, CSS, and JavaScript; +who knows that EscapeSet is applied should be able to look at a {{.}} +and correctly infer what sanitization happens." +*/ +package html diff --git a/libgo/go/exp/template/html/error.go b/libgo/go/exp/template/html/error.go new file mode 100644 index 00000000000..5515bfe68dd --- /dev/null +++ b/libgo/go/exp/template/html/error.go @@ -0,0 +1,213 @@ +// 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 html + +import ( + "fmt" +) + +// Error describes a problem encountered during template Escaping. +type Error struct { + // ErrorCode describes the kind of error. + ErrorCode ErrorCode + // Name is the name of the template in which the error was encountered. + Name string + // Line is the line number of the error in the template source or 0. + Line int + // Description is a human-readable description of the problem. + Description string +} + +// ErrorCode is a code for a kind of error. +type ErrorCode int + +// We define codes for each error that manifests while escaping templates, but +// escaped templates may also fail at runtime. +// +// Output: "ZgotmplZ" +// Example: +// <img src="{{.X}}"> +// where {{.X}} evaluates to `javascript:...` +// Discussion: +// "ZgotmplZ" is a special value that indicates that unsafe content reached a +// CSS or URL context at runtime. The output of the example will be +// <img src="#ZgotmplZ"> +// If the data comes from a trusted source, use content types to exempt it +// from filtering: URL(`javascript:...`). +const ( + // OK indicates the lack of an error. + OK ErrorCode = iota + + // ErrAmbigContext: "... appears in an ambiguous URL context" + // Example: + // <a href=" + // {{if .C}} + // /path/ + // {{else}} + // /search?q= + // {{end}} + // {{.X}} + // "> + // Discussion: + // {{.X}} is in an ambiguous URL context since, depending on {{.C}}, + // it may be either a URL suffix or a query parameter. + // Moving {{.X}} into the condition removes the ambiguity: + // <a href="{{if .C}}/path/{{.X}}{{else}}/search?q={{.X}}"> + ErrAmbigContext + + // ErrBadHTML: "expected space, attr name, or end of tag, but got ...", + // "... in unquoted attr", "... in attribute name" + // Example: + // <a href = /search?q=foo> + // <href=foo> + // <form na<e=...> + // <option selected< + // Discussion: + // This is often due to a typo in an HTML element, but some runes + // are banned in tag names, attribute names, and unquoted attribute + // values because they can tickle parser ambiguities. + // Quoting all attributes is the best policy. + ErrBadHTML + + // ErrBranchEnd: "{{if}} branches end in different contexts" + // Example: + // {{if .C}}<a href="{{end}}{{.X}} + // Discussion: + // EscapeSet statically examines each possible path when it encounters + // a {{if}}, {{range}}, or {{with}} to escape any following pipelines. + // The example is ambiguous since {{.X}} might be an HTML text node, + // or a URL prefix in an HTML attribute. EscapeSet needs to understand + // the context of {{.X}} to escape it, but that depends on the + // run-time value of {{.C}}. + // + // The problem is usually something like missing quotes or angle + // brackets, or can be avoided by refactoring to put the two contexts + // into different branches of an if, range or with. If the problem + // is in a {{range}} over a collection that should never be empty, + // adding a dummy {{else}} can help. + ErrBranchEnd + + // ErrEndContext: "... ends in a non-text context: ..." + // Examples: + // <div + // <div title="no close quote> + // <script>f() + // Discussion: + // EscapeSet assumes the ouput is a DocumentFragment of HTML. + // Templates that end without closing tags will trigger this error. + // Templates that produce incomplete Fragments should not be named + // in the call to EscapeSet. + // + // If you have a helper template in your set that is not meant to + // produce a document fragment, then do not pass its name to + // EscapeSet(set, ...names). + // + // {{define "main"}} <script>{{template "helper"}}</script> {{end}} + // {{define "helper"}} document.write(' <div title=" ') {{end}} + // + // "helper" does not produce a valid document fragment, though it does + // produce a valid JavaScript Program. + ErrEndContext + + // ErrNoNames: "must specify names of top level templates" + // + // EscapeSet does not assume that all templates in a set produce HTML. + // Some may be helpers that produce snippets of other languages. + // Passing in no template names is most likely an error, + // so EscapeSet(set) will panic. + // If you call EscapeSet with a slice of names, guard it with len: + // + // if len(names) != 0 { + // set, err := EscapeSet(set, ...names) + // } + ErrNoNames + + // ErrNoSuchTemplate: "no such template ..." + // Examples: + // {{define "main"}}<div {{template "attrs"}}>{{end}} + // {{define "attrs"}}href="{{.URL}}"{{end}} + // Discussion: + // EscapeSet looks through template calls to compute the context. + // Here the {{.URL}} in "attrs" must be treated as a URL when called + // from "main", but if "attrs" is not in set when + // EscapeSet(&set, "main") is called, this error will arise. + ErrNoSuchTemplate + + // ErrOutputContext: "cannot compute output context for template ..." + // Examples: + // {{define "t"}}{{if .T}}{{template "t" .T}}{{end}}{{.H}}",{{end}} + // Discussion: + // A recursive template does not end in the same context in which it + // starts, and a reliable output context cannot be computed. + // Look for typos in the named template. + // If the template should not be called in the named start context, + // look for calls to that template in unexpected contexts. + // Maybe refactor recursive templates to not be recursive. + ErrOutputContext + + // ErrPartialCharset: "unfinished JS regexp charset in ..." + // Example: + // <script>var pattern = /foo[{{.Chars}}]/</script> + // Discussion: + // EscapeSet does not support interpolation into regular expression + // literal character sets. + ErrPartialCharset + + // ErrPartialEscape: "unfinished escape sequence in ..." + // Example: + // <script>alert("\{{.X}}")</script> + // Discussion: + // EscapeSet does not support actions following a backslash. + // This is usually an error and there are better solutions; for + // our example + // <script>alert("{{.X}}")</script> + // should work, and if {{.X}} is a partial escape sequence such as + // "xA0", mark the whole sequence as safe content: JSStr(`\xA0`) + ErrPartialEscape + + // ErrRangeLoopReentry: "on range loop re-entry: ..." + // Example: + // {{range .}}<p class={{.}}{{end}} + // Discussion: + // If an iteration through a range would cause it to end in a + // different context than an earlier pass, there is no single context. + // In the example, the <p> tag is missing a '>'. + // EscapeSet cannot tell whether {{.}} is meant to be an HTML class or + // the content of a broken <p> element and complains because the + // second iteration would produce something like + // + // <p class=foo<p class=bar + ErrRangeLoopReentry + + // ErrSlashAmbig: '/' could start a division or regexp. + // Example: + // <script> + // {{if .C}}var x = 1{{end}} + // /-{{.N}}/i.test(x) ? doThis : doThat(); + // </script> + // Discussion: + // The example above could produce `var x = 1/-2/i.test(s)...` + // in which the first '/' is a mathematical division operator or it + // could produce `/-2/i.test(s)` in which the first '/' starts a + // regexp literal. + // Look for missing semicolons inside branches, and maybe add + // parentheses to make it clear which interpretation you intend. + ErrSlashAmbig +) + +func (e *Error) String() string { + if e.Line != 0 { + return fmt.Sprintf("exp/template/html:%s:%d: %s", e.Name, e.Line, e.Description) + } else if e.Name != "" { + return fmt.Sprintf("exp/template/html:%s: %s", e.Name, e.Description) + } + return "exp/template/html: " + e.Description +} + +// errorf creates an error given a format string f and args. +// The template Name still needs to be supplied. +func errorf(k ErrorCode, line int, f string, args ...interface{}) *Error { + return &Error{k, "", line, fmt.Sprintf(f, args...)} +} diff --git a/libgo/go/exp/template/html/escape.go b/libgo/go/exp/template/html/escape.go index e0e87b98d04..74abccecddf 100644 --- a/libgo/go/exp/template/html/escape.go +++ b/libgo/go/exp/template/html/escape.go @@ -2,104 +2,760 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package html is a specialization of exp/template that automates the -// construction of safe HTML output. -// At the moment, the escaping is naive. All dynamic content is assumed to be -// plain text interpolated in an HTML PCDATA context. package html import ( + "bytes" + "fmt" + "html" + "os" "template" "template/parse" ) -// Escape rewrites each action in the template to guarantee the output is -// HTML-escaped. -func Escape(t *template.Template) { - // If the parser shares trees based on common-subexpression - // joining then we will need to avoid multiply escaping the same action. - escapeListNode(t.Tree.Root) +// Escape rewrites each action in the template to guarantee that the output is +// properly escaped. +func Escape(t *template.Template) (*template.Template, os.Error) { + var s template.Set + s.Add(t) + if _, err := EscapeSet(&s, t.Name()); err != nil { + return nil, err + } + // TODO: if s contains cloned dependencies due to self-recursion + // cross-context, error out. + return t, nil } -// escapeNode dispatches to escape<NodeType> helpers by type. -func escapeNode(node parse.Node) { - switch n := node.(type) { - case *parse.ListNode: - escapeListNode(n) - case *parse.TextNode: - // Nothing to do. +// EscapeSet rewrites the template set to guarantee that the output of any of +// the named templates is properly escaped. +// Names should include the names of all templates that might be Executed but +// need not include helper templates. +// If no error is returned, then the named templates have been modified. +// Otherwise the named templates have been rendered unusable. +func EscapeSet(s *template.Set, names ...string) (*template.Set, os.Error) { + if len(names) == 0 { + // TODO: Maybe add a method to Set to enumerate template names + // and use those instead. + return nil, &Error{ErrNoNames, "", 0, "must specify names of top level templates"} + } + e := newEscaper(s) + for _, name := range names { + c, _ := e.escapeTree(context{}, name, 0) + var err os.Error + if c.err != nil { + err, c.err.Name = c.err, name + } else if c.state != stateText { + err = &Error{ErrEndContext, name, 0, fmt.Sprintf("ends in a non-text context: %v", c)} + } + if err != nil { + // Prevent execution of unsafe templates. + for _, name := range names { + if t := s.Template(name); t != nil { + t.Tree = nil + } + } + return nil, err + } + } + e.commit() + return s, nil +} + +// funcMap maps command names to functions that render their inputs safe. +var funcMap = template.FuncMap{ + "exp_template_html_attrescaper": attrEscaper, + "exp_template_html_commentescaper": commentEscaper, + "exp_template_html_cssescaper": cssEscaper, + "exp_template_html_cssvaluefilter": cssValueFilter, + "exp_template_html_htmlnamefilter": htmlNameFilter, + "exp_template_html_htmlescaper": htmlEscaper, + "exp_template_html_jsregexpescaper": jsRegexpEscaper, + "exp_template_html_jsstrescaper": jsStrEscaper, + "exp_template_html_jsvalescaper": jsValEscaper, + "exp_template_html_nospaceescaper": htmlNospaceEscaper, + "exp_template_html_rcdataescaper": rcdataEscaper, + "exp_template_html_urlescaper": urlEscaper, + "exp_template_html_urlfilter": urlFilter, + "exp_template_html_urlnormalizer": urlNormalizer, +} + +// equivEscapers matches contextual escapers to equivalent template builtins. +var equivEscapers = map[string]string{ + "exp_template_html_attrescaper": "html", + "exp_template_html_htmlescaper": "html", + "exp_template_html_nospaceescaper": "html", + "exp_template_html_rcdataescaper": "html", + "exp_template_html_urlescaper": "urlquery", + "exp_template_html_urlnormalizer": "urlquery", +} + +// escaper collects type inferences about templates and changes needed to make +// templates injection safe. +type escaper struct { + // set is the template set being escaped. + set *template.Set + // output[templateName] is the output context for a templateName that + // has been mangled to include its input context. + output map[string]context + // derived[c.mangle(name)] maps to a template derived from the template + // named name templateName for the start context c. + derived map[string]*template.Template + // called[templateName] is a set of called mangled template names. + called map[string]bool + // xxxNodeEdits are the accumulated edits to apply during commit. + // Such edits are not applied immediately in case a template set + // executes a given template in different escaping contexts. + actionNodeEdits map[*parse.ActionNode][]string + templateNodeEdits map[*parse.TemplateNode]string + textNodeEdits map[*parse.TextNode][]byte +} + +// newEscaper creates a blank escaper for the given set. +func newEscaper(s *template.Set) *escaper { + return &escaper{ + s, + map[string]context{}, + map[string]*template.Template{}, + map[string]bool{}, + map[*parse.ActionNode][]string{}, + map[*parse.TemplateNode]string{}, + map[*parse.TextNode][]byte{}, + } +} + +// filterFailsafe is an innocuous word that is emitted in place of unsafe values +// by sanitizer functions. It is not a keyword in any programming language, +// contains no special characters, is not empty, and when it appears in output +// it is distinct enough that a developer can find the source of the problem +// via a search engine. +const filterFailsafe = "ZgotmplZ" + +// escape escapes a template node. +func (e *escaper) escape(c context, n parse.Node) context { + switch n := n.(type) { case *parse.ActionNode: - escapeActionNode(n) + return e.escapeAction(c, n) case *parse.IfNode: - escapeIfNode(n) + return e.escapeBranch(c, &n.BranchNode, "if") + case *parse.ListNode: + return e.escapeList(c, n) case *parse.RangeNode: - escapeRangeNode(n) + return e.escapeBranch(c, &n.BranchNode, "range") case *parse.TemplateNode: - // Nothing to do. + return e.escapeTemplate(c, n) + case *parse.TextNode: + return e.escapeText(c, n) case *parse.WithNode: - escapeWithNode(n) + return e.escapeBranch(c, &n.BranchNode, "with") + } + panic("escaping " + n.String() + " is unimplemented") +} + +// escapeAction escapes an action template node. +func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { + if len(n.Pipe.Decl) != 0 { + // A local variable assignment, not an interpolation. + return c + } + c = nudge(c) + s := make([]string, 0, 3) + switch c.state { + case stateError: + return c + case stateURL, stateCSSDqStr, stateCSSSqStr, stateCSSDqURL, stateCSSSqURL, stateCSSURL: + switch c.urlPart { + case urlPartNone: + s = append(s, "exp_template_html_urlfilter") + fallthrough + case urlPartPreQuery: + switch c.state { + case stateCSSDqStr, stateCSSSqStr: + s = append(s, "exp_template_html_cssescaper") + default: + s = append(s, "exp_template_html_urlnormalizer") + } + case urlPartQueryOrFrag: + s = append(s, "exp_template_html_urlescaper") + case urlPartUnknown: + return context{ + state: stateError, + err: errorf(ErrAmbigContext, n.Line, "%s appears in an ambiguous URL context", n), + } + default: + panic(c.urlPart.String()) + } + case stateJS: + s = append(s, "exp_template_html_jsvalescaper") + // A slash after a value starts a div operator. + c.jsCtx = jsCtxDivOp + case stateJSDqStr, stateJSSqStr: + s = append(s, "exp_template_html_jsstrescaper") + case stateJSRegexp: + s = append(s, "exp_template_html_jsregexpescaper") + case stateCSS: + s = append(s, "exp_template_html_cssvaluefilter") + case stateText: + s = append(s, "exp_template_html_htmlescaper") + case stateRCDATA: + s = append(s, "exp_template_html_rcdataescaper") + case stateAttr: + // Handled below in delim check. + case stateAttrName, stateTag: + c.state = stateAttrName + s = append(s, "exp_template_html_htmlnamefilter") default: - panic("handling for " + node.String() + " not implemented") - // TODO: Handle other inner node types. + if isComment(c.state) { + s = append(s, "exp_template_html_commentescaper") + } else { + panic("unexpected state " + c.state.String()) + } } + switch c.delim { + case delimNone: + // No extra-escaping needed for raw text content. + case delimSpaceOrTagEnd: + s = append(s, "exp_template_html_nospaceescaper") + default: + s = append(s, "exp_template_html_attrescaper") + } + e.editActionNode(n, s) + return c } -// escapeListNode recursively escapes its input's children. -func escapeListNode(node *parse.ListNode) { - if node == nil { +// ensurePipelineContains ensures that the pipeline has commands with +// the identifiers in s in order. +// If the pipeline already has some of the sanitizers, do not interfere. +// For example, if p is (.X | html) and s is ["escapeJSVal", "html"] then it +// has one matching, "html", and one to insert, "escapeJSVal", to produce +// (.X | escapeJSVal | html). +func ensurePipelineContains(p *parse.PipeNode, s []string) { + if len(s) == 0 { return } - children := node.Nodes - for _, child := range children { - escapeNode(child) + n := len(p.Cmds) + // Find the identifiers at the end of the command chain. + idents := p.Cmds + for i := n - 1; i >= 0; i-- { + if cmd := p.Cmds[i]; len(cmd.Args) != 0 { + if id, ok := cmd.Args[0].(*parse.IdentifierNode); ok { + if id.Ident == "noescape" { + return + } + continue + } + } + idents = p.Cmds[i+1:] + } + dups := 0 + for _, id := range idents { + if escFnsEq(s[dups], (id.Args[0].(*parse.IdentifierNode)).Ident) { + dups++ + if dups == len(s) { + return + } + } + } + newCmds := make([]*parse.CommandNode, n-len(idents), n+len(s)-dups) + copy(newCmds, p.Cmds) + // Merge existing identifier commands with the sanitizers needed. + for _, id := range idents { + i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s, escFnsEq) + if i != -1 { + for _, name := range s[:i] { + newCmds = appendCmd(newCmds, newIdentCmd(name)) + } + s = s[i+1:] + } + newCmds = appendCmd(newCmds, id) + } + // Create any remaining sanitizers. + for _, name := range s { + newCmds = appendCmd(newCmds, newIdentCmd(name)) } + p.Cmds = newCmds } -// escapeActionNode adds a pipeline call to the end that escapes the result -// of the expression before it is interpolated into the template output. -func escapeActionNode(node *parse.ActionNode) { - pipe := node.Pipe +// redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x) +// for all x. +var redundantFuncs = map[string]map[string]bool{ + "exp_template_html_commentescaper": { + "exp_template_html_attrescaper": true, + "exp_template_html_nospaceescaper": true, + "exp_template_html_htmlescaper": true, + }, + "exp_template_html_cssescaper": { + "exp_template_html_attrescaper": true, + }, + "exp_template_html_jsregexpescaper": { + "exp_template_html_attrescaper": true, + }, + "exp_template_html_jsstrescaper": { + "exp_template_html_attrescaper": true, + }, + "exp_template_html_urlescaper": { + "exp_template_html_urlnormalizer": true, + }, +} - cmds := pipe.Cmds - nCmds := len(cmds) +// appendCmd appends the given command to the end of the command pipeline +// unless it is redundant with the last command. +func appendCmd(cmds []*parse.CommandNode, cmd *parse.CommandNode) []*parse.CommandNode { + if n := len(cmds); n != 0 { + last, ok := cmds[n-1].Args[0].(*parse.IdentifierNode) + next, _ := cmd.Args[0].(*parse.IdentifierNode) + if ok && redundantFuncs[last.Ident][next.Ident] { + return cmds + } + } + return append(cmds, cmd) +} - // If it already has an escaping command, do not interfere. - if nCmds != 0 { - if lastCmd := cmds[nCmds-1]; len(lastCmd.Args) != 0 { - // TODO: Recognize url and js as escaping functions once - // we have enough context to know whether additional - // escaping is necessary. - if arg, ok := lastCmd.Args[0].(*parse.IdentifierNode); ok && arg.Ident == "html" { - return - } +// indexOfStr is the first i such that eq(s, strs[i]) or -1 if s was not found. +func indexOfStr(s string, strs []string, eq func(a, b string) bool) int { + for i, t := range strs { + if eq(s, t) { + return i } } + return -1 +} - htmlEscapeCommand := parse.CommandNode{ +// escFnsEq returns whether the two escaping functions are equivalent. +func escFnsEq(a, b string) bool { + if e := equivEscapers[a]; e != "" { + a = e + } + if e := equivEscapers[b]; e != "" { + b = e + } + return a == b +} + +// newIdentCmd produces a command containing a single identifier node. +func newIdentCmd(identifier string) *parse.CommandNode { + return &parse.CommandNode{ NodeType: parse.NodeCommand, - Args: []parse.Node{parse.NewIdentifier("html")}, + Args: []parse.Node{parse.NewIdentifier(identifier)}, } +} - node.Pipe.Cmds = append(node.Pipe.Cmds, &htmlEscapeCommand) +// nudge returns the context that would result from following empty string +// transitions from the input context. +// For example, parsing: +// `<a href=` +// will end in context{stateBeforeValue, attrURL}, but parsing one extra rune: +// `<a href=x` +// will end in context{stateURL, delimSpaceOrTagEnd, ...}. +// There are two transitions that happen when the 'x' is seen: +// (1) Transition from a before-value state to a start-of-value state without +// consuming any character. +// (2) Consume 'x' and transition past the first value character. +// In this case, nudging produces the context after (1) happens. +func nudge(c context) context { + switch c.state { + case stateTag: + // In `<foo {{.}}`, the action should emit an attribute. + c.state = stateAttrName + case stateBeforeValue: + // In `<foo bar={{.}}`, the action is an undelimited value. + c.state, c.delim, c.attr = attrStartStates[c.attr], delimSpaceOrTagEnd, attrNone + case stateAfterName: + // In `<foo bar {{.}}`, the action is an attribute name. + c.state, c.attr = stateAttrName, attrNone + } + return c } -// escapeIfNode recursively escapes the if and then clauses but leaves the -// condition unchanged. -func escapeIfNode(node *parse.IfNode) { - escapeListNode(node.List) - escapeListNode(node.ElseList) +// join joins the two contexts of a branch template node. The result is an +// error context if either of the input contexts are error contexts, or if the +// the input contexts differ. +func join(a, b context, line int, nodeName string) context { + if a.state == stateError { + return a + } + if b.state == stateError { + return b + } + if a.eq(b) { + return a + } + + c := a + c.urlPart = b.urlPart + if c.eq(b) { + // The contexts differ only by urlPart. + c.urlPart = urlPartUnknown + return c + } + + c = a + c.jsCtx = b.jsCtx + if c.eq(b) { + // The contexts differ only by jsCtx. + c.jsCtx = jsCtxUnknown + return c + } + + // Allow a nudged context to join with an unnudged one. + // This means that + // <p title={{if .C}}{{.}}{{end}} + // ends in an unquoted value state even though the else branch + // ends in stateBeforeValue. + if c, d := nudge(a), nudge(b); !(c.eq(a) && d.eq(b)) { + if e := join(c, d, line, nodeName); e.state != stateError { + return e + } + } + + return context{ + state: stateError, + err: errorf(ErrBranchEnd, line, "{{%s}} branches end in different contexts: %v, %v", nodeName, a, b), + } } -// escapeRangeNode recursively escapes the loop body and else clause but -// leaves the series unchanged. -func escapeRangeNode(node *parse.RangeNode) { - escapeListNode(node.List) - escapeListNode(node.ElseList) +// escapeBranch escapes a branch template node: "if", "range" and "with". +func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) context { + c0 := e.escapeList(c, n.List) + if nodeName == "range" && c0.state != stateError { + // The "true" branch of a "range" node can execute multiple times. + // We check that executing n.List once results in the same context + // as executing n.List twice. + c1, _ := e.escapeListConditionally(c0, n.List, nil) + c0 = join(c0, c1, n.Line, nodeName) + if c0.state == stateError { + // Make clear that this is a problem on loop re-entry + // since developers tend to overlook that branch when + // debugging templates. + c0.err.Line = n.Line + c0.err.Description = "on range loop re-entry: " + c0.err.Description + return c0 + } + } + c1 := e.escapeList(c, n.ElseList) + return join(c0, c1, n.Line, nodeName) } -// escapeWithNode recursively escapes the scope body and else clause but -// leaves the pipeline unchanged. -func escapeWithNode(node *parse.WithNode) { - escapeListNode(node.List) - escapeListNode(node.ElseList) +// escapeList escapes a list template node. +func (e *escaper) escapeList(c context, n *parse.ListNode) context { + if n == nil { + return c + } + for _, m := range n.Nodes { + c = e.escape(c, m) + } + return c +} + +// escapeListConditionally escapes a list node but only preserves edits and +// inferences in e if the inferences and output context satisfy filter. +// It returns the best guess at an output context, and the result of the filter +// which is the same as whether e was updated. +func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) { + e1 := newEscaper(e.set) + // Make type inferences available to f. + for k, v := range e.output { + e1.output[k] = v + } + c = e1.escapeList(c, n) + ok := filter != nil && filter(e1, c) + if ok { + // Copy inferences and edits from e1 back into e. + for k, v := range e1.output { + e.output[k] = v + } + for k, v := range e1.derived { + e.derived[k] = v + } + for k, v := range e1.called { + e.called[k] = v + } + for k, v := range e1.actionNodeEdits { + e.editActionNode(k, v) + } + for k, v := range e1.templateNodeEdits { + e.editTemplateNode(k, v) + } + for k, v := range e1.textNodeEdits { + e.editTextNode(k, v) + } + } + return c, ok +} + +// escapeTemplate escapes a {{template}} call node. +func (e *escaper) escapeTemplate(c context, n *parse.TemplateNode) context { + c, name := e.escapeTree(c, n.Name, n.Line) + if name != n.Name { + e.editTemplateNode(n, name) + } + return c +} + +// escapeTree escapes the named template starting in the given context as +// necessary and returns its output context. +func (e *escaper) escapeTree(c context, name string, line int) (context, string) { + // Mangle the template name with the input context to produce a reliable + // identifier. + dname := c.mangle(name) + e.called[dname] = true + if out, ok := e.output[dname]; ok { + // Already escaped. + return out, dname + } + t := e.template(name) + if t == nil { + return context{ + state: stateError, + err: errorf(ErrNoSuchTemplate, line, "no such template %s", name), + }, dname + } + if dname != name { + // Use any template derived during an earlier call to EscapeSet + // with different top level templates, or clone if necessary. + dt := e.template(dname) + if dt == nil { + dt = template.New(dname) + dt.Tree = &parse.Tree{Name: dname, Root: cloneList(t.Root)} + e.derived[dname] = dt + } + t = dt + } + return e.computeOutCtx(c, t), dname +} + +// computeOutCtx takes a template and its start context and computes the output +// context while storing any inferences in e. +func (e *escaper) computeOutCtx(c context, t *template.Template) context { + // Propagate context over the body. + c1, ok := e.escapeTemplateBody(c, t) + if !ok { + // Look for a fixed point by assuming c1 as the output context. + if c2, ok2 := e.escapeTemplateBody(c1, t); ok2 { + c1, ok = c2, true + } + // Use c1 as the error context if neither assumption worked. + } + if !ok && c1.state != stateError { + return context{ + state: stateError, + // TODO: Find the first node with a line in t.Tree.Root + err: errorf(ErrOutputContext, 0, "cannot compute output context for template %s", t.Name()), + } + } + return c1 +} + +// escapeTemplateBody escapes the given template assuming the given output +// context, and returns the best guess at the output context and whether the +// assumption was correct. +func (e *escaper) escapeTemplateBody(c context, t *template.Template) (context, bool) { + filter := func(e1 *escaper, c1 context) bool { + if c1.state == stateError { + // Do not update the input escaper, e. + return false + } + if !e1.called[t.Name()] { + // If t is not recursively called, then c1 is an + // accurate output context. + return true + } + // c1 is accurate if it matches our assumed output context. + return c.eq(c1) + } + // We need to assume an output context so that recursive template calls + // take the fast path out of escapeTree instead of infinitely recursing. + // Naively assuming that the input context is the same as the output + // works >90% of the time. + e.output[t.Name()] = c + return e.escapeListConditionally(c, t.Tree.Root, filter) +} + +// delimEnds maps each delim to a string of characters that terminate it. +var delimEnds = [...]string{ + delimDoubleQuote: `"`, + delimSingleQuote: "'", + // Determined empirically by running the below in various browsers. + // var div = document.createElement("DIV"); + // for (var i = 0; i < 0x10000; ++i) { + // div.innerHTML = "<span title=x" + String.fromCharCode(i) + "-bar>"; + // if (div.getElementsByTagName("SPAN")[0].title.indexOf("bar") < 0) + // document.write("<p>U+" + i.toString(16)); + // } + delimSpaceOrTagEnd: " \t\n\f\r>", +} + +var doctypeBytes = []byte("<!DOCTYPE") + +// escapeText escapes a text template node. +func (e *escaper) escapeText(c context, n *parse.TextNode) context { + s, written, i, b := n.Text, 0, 0, new(bytes.Buffer) + for i != len(s) { + c1, nread := contextAfterText(c, s[i:]) + i1 := i + nread + if c.state == stateText || c.state == stateRCDATA { + end := i1 + if c1.state != c.state { + for j := end - 1; j >= i; j-- { + if s[j] == '<' { + end = j + break + } + } + } + for j := i; j < end; j++ { + if s[j] == '<' && !bytes.HasPrefix(s[j:], doctypeBytes) { + b.Write(s[written:j]) + b.WriteString("<") + written = j + 1 + } + } + } else if isComment(c.state) && c.delim == delimNone { + switch c.state { + case stateJSBlockCmt: + // http://es5.github.com/#x7.4: + // "Comments behave like white space and are + // discarded except that, if a MultiLineComment + // contains a line terminator character, then + // the entire comment is considered to be a + // LineTerminator for purposes of parsing by + // the syntactic grammar." + if bytes.IndexAny(s[written:i1], "\n\r\u2028\u2029") != -1 { + b.WriteByte('\n') + } else { + b.WriteByte(' ') + } + case stateCSSBlockCmt: + b.WriteByte(' ') + } + written = i1 + } + if c.state != c1.state && isComment(c1.state) && c1.delim == delimNone { + // Preserve the portion between written and the comment start. + cs := i1 - 2 + if c1.state == stateHTMLCmt { + // "<!--" instead of "/*" or "//" + cs -= 2 + } + b.Write(s[written:cs]) + written = i1 + } + if i == i1 && c.state == c1.state { + panic(fmt.Sprintf("infinite loop from %v to %v on %q..%q", c, c1, s[:i], s[i:])) + } + c, i = c1, i1 + } + + if written != 0 && c.state != stateError { + if !isComment(c.state) || c.delim != delimNone { + b.Write(n.Text[written:]) + } + e.editTextNode(n, b.Bytes()) + } + return c +} + +// contextAfterText starts in context c, consumes some tokens from the front of +// s, then returns the context after those tokens and the unprocessed suffix. +func contextAfterText(c context, s []byte) (context, int) { + if c.delim == delimNone { + c1, i := tSpecialTagEnd(c, s) + if i == 0 { + // A special end tag (`</script>`) has been seen and + // all content preceding it has been consumed. + return c1, 0 + } + // Consider all content up to any end tag. + return transitionFunc[c.state](c, s[:i]) + } + + i := bytes.IndexAny(s, delimEnds[c.delim]) + if i == -1 { + i = len(s) + } + if c.delim == delimSpaceOrTagEnd { + // http://www.w3.org/TR/html5/tokenization.html#attribute-value-unquoted-state + // lists the runes below as error characters. + // Error out because HTML parsers may differ on whether + // "<a id= onclick=f(" ends inside id's or onchange's value, + // "<a class=`foo " ends inside a value, + // "<a style=font:'Arial'" needs open-quote fixup. + // IE treats '`' as a quotation character. + if j := bytes.IndexAny(s[:i], "\"'<=`"); j >= 0 { + return context{ + state: stateError, + err: errorf(ErrBadHTML, 0, "%q in unquoted attr: %q", s[j:j+1], s[:i]), + }, len(s) + } + } + if i == len(s) { + // Remain inside the attribute. + // Decode the value so non-HTML rules can easily handle + // <button onclick="alert("Hi!")"> + // without having to entity decode token boundaries. + for u := []byte(html.UnescapeString(string(s))); len(u) != 0; { + c1, i1 := transitionFunc[c.state](c, u) + c, u = c1, u[i1:] + } + return c, len(s) + } + if c.delim != delimSpaceOrTagEnd { + // Consume any quote. + i++ + } + // On exiting an attribute, we discard all state information + // except the state and element. + return context{state: stateTag, element: c.element}, i +} + +// editActionNode records a change to an action pipeline for later commit. +func (e *escaper) editActionNode(n *parse.ActionNode, cmds []string) { + if _, ok := e.actionNodeEdits[n]; ok { + panic(fmt.Sprintf("node %s shared between templates", n)) + } + e.actionNodeEdits[n] = cmds +} + +// editTemplateNode records a change to a {{template}} callee for later commit. +func (e *escaper) editTemplateNode(n *parse.TemplateNode, callee string) { + if _, ok := e.templateNodeEdits[n]; ok { + panic(fmt.Sprintf("node %s shared between templates", n)) + } + e.templateNodeEdits[n] = callee +} + +// editTextNode records a change to a text node for later commit. +func (e *escaper) editTextNode(n *parse.TextNode, text []byte) { + if _, ok := e.textNodeEdits[n]; ok { + panic(fmt.Sprintf("node %s shared between templates", n)) + } + e.textNodeEdits[n] = text +} + +// commit applies changes to actions and template calls needed to contextually +// autoescape content and adds any derived templates to the set. +func (e *escaper) commit() { + for name, _ := range e.output { + e.template(name).Funcs(funcMap) + } + for _, t := range e.derived { + e.set.Add(t) + } + for n, s := range e.actionNodeEdits { + ensurePipelineContains(n.Pipe, s) + } + for n, name := range e.templateNodeEdits { + n.Name = name + } + for n, s := range e.textNodeEdits { + n.Text = s + } +} + +// template returns the named template given a mangled template name. +func (e *escaper) template(name string) *template.Template { + t := e.set.Template(name) + if t == nil { + t = e.derived[name] + } + return t } diff --git a/libgo/go/exp/template/html/escape_test.go b/libgo/go/exp/template/html/escape_test.go index 345a752a896..a4ea7596cd1 100644 --- a/libgo/go/exp/template/html/escape_test.go +++ b/libgo/go/exp/template/html/escape_test.go @@ -6,70 +6,1618 @@ package html import ( "bytes" + "fmt" + "json" + "os" + "strings" "template" + "template/parse" "testing" ) -type data struct { - F, T bool - C, G, H string - A, E []string +type badMarshaler struct{} + +func (x *badMarshaler) MarshalJSON() ([]byte, os.Error) { + // Keys in valid JSON must be double quoted as must all strings. + return []byte("{ foo: 'not quite valid JSON' }"), nil } -var testData = data{ - F: false, - T: true, - C: "<Cincinatti>", - G: "<Goodbye>", - H: "<Hello>", - A: []string{"<a>", "<b>"}, - E: []string{}, +type goodMarshaler struct{} + +func (x *goodMarshaler) MarshalJSON() ([]byte, os.Error) { + return []byte(`{ "<foo>": "O'Reilly" }`), nil } -type testCase struct { - name string - input string - output string +func TestEscape(t *testing.T) { + var data = struct { + F, T bool + C, G, H string + A, E []string + B, M json.Marshaler + N int + Z *int + W HTML + }{ + F: false, + T: true, + C: "<Cincinatti>", + G: "<Goodbye>", + H: "<Hello>", + A: []string{"<a>", "<b>"}, + E: []string{}, + N: 42, + B: &badMarshaler{}, + M: &goodMarshaler{}, + Z: nil, + W: HTML(`¡<b class="foo">Hello</b>, <textarea>O'World</textarea>!`), + } + + tests := []struct { + name string + input string + output string + }{ + { + "if", + "{{if .T}}Hello{{end}}, {{.C}}!", + "Hello, <Cincinatti>!", + }, + { + "else", + "{{if .F}}{{.H}}{{else}}{{.G}}{{end}}!", + "<Goodbye>!", + }, + { + "overescaping1", + "Hello, {{.C | html}}!", + "Hello, <Cincinatti>!", + }, + { + "overescaping2", + "Hello, {{html .C}}!", + "Hello, <Cincinatti>!", + }, + { + "overescaping3", + "{{with .C}}{{$msg := .}}Hello, {{$msg}}!{{end}}", + "Hello, <Cincinatti>!", + }, + { + "assignment", + "{{if $x := .H}}{{$x}}{{end}}", + "<Hello>", + }, + { + "withBody", + "{{with .H}}{{.}}{{end}}", + "<Hello>", + }, + { + "withElse", + "{{with .E}}{{.}}{{else}}{{.H}}{{end}}", + "<Hello>", + }, + { + "rangeBody", + "{{range .A}}{{.}}{{end}}", + "<a><b>", + }, + { + "rangeElse", + "{{range .E}}{{.}}{{else}}{{.H}}{{end}}", + "<Hello>", + }, + { + "nonStringValue", + "{{.T}}", + "true", + }, + { + "constant", + `<a href="/search?q={{"'a<b'"}}">`, + `<a href="/search?q=%27a%3cb%27">`, + }, + { + "multipleAttrs", + "<a b=1 c={{.H}}>", + "<a b=1 c=<Hello>>", + }, + { + "urlStartRel", + `<a href='{{"/foo/bar?a=b&c=d"}}'>`, + `<a href='/foo/bar?a=b&c=d'>`, + }, + { + "urlStartAbsOk", + `<a href='{{"http://example.com/foo/bar?a=b&c=d"}}'>`, + `<a href='http://example.com/foo/bar?a=b&c=d'>`, + }, + { + "protocolRelativeURLStart", + `<a href='{{"//example.com:8000/foo/bar?a=b&c=d"}}'>`, + `<a href='//example.com:8000/foo/bar?a=b&c=d'>`, + }, + { + "pathRelativeURLStart", + `<a href="{{"/javascript:80/foo/bar"}}">`, + `<a href="/javascript:80/foo/bar">`, + }, + { + "dangerousURLStart", + `<a href='{{"javascript:alert(%22pwned%22)"}}'>`, + `<a href='#ZgotmplZ'>`, + }, + { + "dangerousURLStart2", + `<a href=' {{"javascript:alert(%22pwned%22)"}}'>`, + `<a href=' #ZgotmplZ'>`, + }, + { + "nonHierURL", + `<a href={{"mailto:Muhammed \"The Greatest\" Ali <m.ali@example.com>"}}>`, + `<a href=mailto:Muhammed%20%22The%20Greatest%22%20Ali%20%3cm.ali@example.com%3e>`, + }, + { + "urlPath", + `<a href='http://{{"javascript:80"}}/foo'>`, + `<a href='http://javascript:80/foo'>`, + }, + { + "urlQuery", + `<a href='/search?q={{.H}}'>`, + `<a href='/search?q=%3cHello%3e'>`, + }, + { + "urlFragment", + `<a href='/faq#{{.H}}'>`, + `<a href='/faq#%3cHello%3e'>`, + }, + { + "urlBranch", + `<a href="{{if .F}}/foo?a=b{{else}}/bar{{end}}">`, + `<a href="/bar">`, + }, + { + "urlBranchConflictMoot", + `<a href="{{if .T}}/foo?a={{else}}/bar#{{end}}{{.C}}">`, + `<a href="/foo?a=%3cCincinatti%3e">`, + }, + { + "jsStrValue", + "<button onclick='alert({{.H}})'>", + `<button onclick='alert("\u003cHello\u003e")'>`, + }, + { + "jsNumericValue", + "<button onclick='alert({{.N}})'>", + `<button onclick='alert( 42 )'>`, + }, + { + "jsBoolValue", + "<button onclick='alert({{.T}})'>", + `<button onclick='alert( true )'>`, + }, + { + "jsNilValue", + "<button onclick='alert(typeof{{.Z}})'>", + `<button onclick='alert(typeof null )'>`, + }, + { + "jsObjValue", + "<button onclick='alert({{.A}})'>", + `<button onclick='alert(["\u003ca\u003e","\u003cb\u003e"])'>`, + }, + { + "jsObjValueScript", + "<script>alert({{.A}})</script>", + `<script>alert(["\u003ca\u003e","\u003cb\u003e"])</script>`, + }, + { + "jsObjValueNotOverEscaped", + "<button onclick='alert({{.A | html}})'>", + `<button onclick='alert(["\u003ca\u003e","\u003cb\u003e"])'>`, + }, + { + "jsStr", + "<button onclick='alert("{{.H}}")'>", + `<button onclick='alert("\x3cHello\x3e")'>`, + }, + { + "badMarshaller", + `<button onclick='alert(1/{{.B}}in numbers)'>`, + `<button onclick='alert(1/ /* json: error calling MarshalJSON for type *html.badMarshaler: invalid character 'f' looking for beginning of object key string */null in numbers)'>`, + }, + { + "jsMarshaller", + `<button onclick='alert({{.M}})'>`, + `<button onclick='alert({"<foo>":"O'Reilly"})'>`, + }, + { + "jsStrNotUnderEscaped", + "<button onclick='alert({{.C | urlquery}})'>", + // URL escaped, then quoted for JS. + `<button onclick='alert("%3CCincinatti%3E")'>`, + }, + { + "jsRe", + `<button onclick='alert(/{{"foo+bar"}}/.test(""))'>`, + `<button onclick='alert(/foo\x2bbar/.test(""))'>`, + }, + { + "jsReBlank", + `<script>alert(/{{""}}/.test(""));</script>`, + `<script>alert(/(?:)/.test(""));</script>`, + }, + { + "jsReAmbigOk", + `<script>{{if true}}var x = 1{{end}}</script>`, + // The {if} ends in an ambiguous jsCtx but there is + // no slash following so we shouldn't care. + `<script>var x = 1</script>`, + }, + { + "styleBidiKeywordPassed", + `<p style="dir: {{"ltr"}}">`, + `<p style="dir: ltr">`, + }, + { + "styleBidiPropNamePassed", + `<p style="border-{{"left"}}: 0; border-{{"right"}}: 1in">`, + `<p style="border-left: 0; border-right: 1in">`, + }, + { + "styleExpressionBlocked", + `<p style="width: {{"expression(alert(1337))"}}">`, + `<p style="width: ZgotmplZ">`, + }, + { + "styleTagSelectorPassed", + `<style>{{"p"}} { color: pink }</style>`, + `<style>p { color: pink }</style>`, + }, + { + "styleIDPassed", + `<style>p{{"#my-ID"}} { font: Arial }</style>`, + `<style>p#my-ID { font: Arial }</style>`, + }, + { + "styleClassPassed", + `<style>p{{".my_class"}} { font: Arial }</style>`, + `<style>p.my_class { font: Arial }</style>`, + }, + { + "styleQuantityPassed", + `<a style="left: {{"2em"}}; top: {{0}}">`, + `<a style="left: 2em; top: 0">`, + }, + { + "stylePctPassed", + `<table style=width:{{"100%"}}>`, + `<table style=width:100%>`, + }, + { + "styleColorPassed", + `<p style="color: {{"#8ff"}}; background: {{"#000"}}">`, + `<p style="color: #8ff; background: #000">`, + }, + { + "styleObfuscatedExpressionBlocked", + `<p style="width: {{" e\78preS\0Sio/**/n(alert(1337))"}}">`, + `<p style="width: ZgotmplZ">`, + }, + { + "styleMozBindingBlocked", + `<p style="{{"-moz-binding(alert(1337))"}}: ...">`, + `<p style="ZgotmplZ: ...">`, + }, + { + "styleObfuscatedMozBindingBlocked", + `<p style="{{" -mo\7a-B\0I/**/nding(alert(1337))"}}: ...">`, + `<p style="ZgotmplZ: ...">`, + }, + { + "styleFontNameString", + `<p style='font-family: "{{"Times New Roman"}}"'>`, + `<p style='font-family: "Times New Roman"'>`, + }, + { + "styleFontNameString", + `<p style='font-family: "{{"Times New Roman"}}", "{{"sans-serif"}}"'>`, + `<p style='font-family: "Times New Roman", "sans-serif"'>`, + }, + { + "styleFontNameUnquoted", + `<p style='font-family: {{"Times New Roman"}}'>`, + `<p style='font-family: Times New Roman'>`, + }, + { + "styleURLQueryEncoded", + `<p style="background: url(/img?name={{"O'Reilly Animal(1)<2>.png"}})">`, + `<p style="background: url(/img?name=O%27Reilly%20Animal%281%29%3c2%3e.png)">`, + }, + { + "styleQuotedURLQueryEncoded", + `<p style="background: url('/img?name={{"O'Reilly Animal(1)<2>.png"}}')">`, + `<p style="background: url('/img?name=O%27Reilly%20Animal%281%29%3c2%3e.png')">`, + }, + { + "styleStrQueryEncoded", + `<p style="background: '/img?name={{"O'Reilly Animal(1)<2>.png"}}'">`, + `<p style="background: '/img?name=O%27Reilly%20Animal%281%29%3c2%3e.png'">`, + }, + { + "styleURLBadProtocolBlocked", + `<a style="background: url('{{"javascript:alert(1337)"}}')">`, + `<a style="background: url('#ZgotmplZ')">`, + }, + { + "styleStrBadProtocolBlocked", + `<a style="background: '{{"vbscript:alert(1337)"}}'">`, + `<a style="background: '#ZgotmplZ'">`, + }, + { + "styleStrEncodedProtocolEncoded", + `<a style="background: '{{"javascript\\3a alert(1337)"}}'">`, + // The CSS string 'javascript\\3a alert(1337)' does not contains a colon. + `<a style="background: 'javascript\\3a alert\28 1337\29 '">`, + }, + { + "styleURLGoodProtocolPassed", + `<a style="background: url('{{"http://oreilly.com/O'Reilly Animals(1)<2>;{}.html"}}')">`, + `<a style="background: url('http://oreilly.com/O%27Reilly%20Animals%281%29%3c2%3e;%7b%7d.html')">`, + }, + { + "styleStrGoodProtocolPassed", + `<a style="background: '{{"http://oreilly.com/O'Reilly Animals(1)<2>;{}.html"}}'">`, + `<a style="background: 'http\3a\2f\2foreilly.com\2fO\27Reilly Animals\28 1\29\3c 2\3e\3b\7b\7d.html'">`, + }, + { + "styleURLEncodedForHTMLInAttr", + `<a style="background: url('{{"/search?img=foo&size=icon"}}')">`, + `<a style="background: url('/search?img=foo&size=icon')">`, + }, + { + "styleURLNotEncodedForHTMLInCdata", + `<style>body { background: url('{{"/search?img=foo&size=icon"}}') }</style>`, + `<style>body { background: url('/search?img=foo&size=icon') }</style>`, + }, + { + "styleURLMixedCase", + `<p style="background: URL(#{{.H}})">`, + `<p style="background: URL(#%3cHello%3e)">`, + }, + { + "stylePropertyPairPassed", + `<a style='{{"color: red"}}'>`, + `<a style='color: red'>`, + }, + { + "styleStrSpecialsEncoded", + `<a style="font-family: '{{"/**/'\";:// \\"}}', "{{"/**/'\";:// \\"}}"">`, + `<a style="font-family: '\2f**\2f\27\22\3b\3a\2f\2f \\', "\2f**\2f\27\22\3b\3a\2f\2f \\"">`, + }, + { + "styleURLSpecialsEncoded", + `<a style="border-image: url({{"/**/'\";:// \\"}}), url("{{"/**/'\";:// \\"}}"), url('{{"/**/'\";:// \\"}}'), 'http://www.example.com/?q={{"/**/'\";:// \\"}}''">`, + `<a style="border-image: url(/**/%27%22;://%20%5c), url("/**/%27%22;://%20%5c"), url('/**/%27%22;://%20%5c'), 'http://www.example.com/?q=%2f%2a%2a%2f%27%22%3b%3a%2f%2f%20%5c''">`, + }, + { + "HTML comment", + "<b>Hello, <!-- name of world -->{{.C}}</b>", + "<b>Hello, <Cincinatti></b>", + }, + { + "HTML comment not first < in text node.", + "<<!-- -->!--", + "<!--", + }, + { + "HTML normalization 1", + "a < b", + "a < b", + }, + { + "HTML normalization 2", + "a << b", + "a << b", + }, + { + "HTML normalization 3", + "a<<!-- --><!-- -->b", + "a<b", + }, + { + "HTML doctype not normalized", + "<!DOCTYPE html>Hello, World!", + "<!DOCTYPE html>Hello, World!", + }, + { + "No doctype injection", + `<!{{"DOCTYPE"}}`, + "<!DOCTYPE", + }, + { + "Split HTML comment", + "<b>Hello, <!-- name of {{if .T}}city -->{{.C}}{{else}}world -->{{.W}}{{end}}</b>", + "<b>Hello, <Cincinatti></b>", + }, + { + "JS line comment", + "<script>for (;;) { if (c()) break// foo not a label\n" + + "foo({{.T}});}</script>", + "<script>for (;;) { if (c()) break\n" + + "foo( true );}</script>", + }, + { + "JS multiline block comment", + "<script>for (;;) { if (c()) break/* foo not a label\n" + + " */foo({{.T}});}</script>", + // Newline separates break from call. If newline + // removed, then break will consume label leaving + // code invalid. + "<script>for (;;) { if (c()) break\n" + + "foo( true );}</script>", + }, + { + "JS single-line block comment", + "<script>for (;;) {\n" + + "if (c()) break/* foo a label */foo;" + + "x({{.T}});}</script>", + // Newline separates break from call. If newline + // removed, then break will consume label leaving + // code invalid. + "<script>for (;;) {\n" + + "if (c()) break foo;" + + "x( true );}</script>", + }, + { + "JS block comment flush with mathematical division", + "<script>var a/*b*//c\nd</script>", + "<script>var a /c\nd</script>", + }, + { + "JS mixed comments", + "<script>var a/*b*///c\nd</script>", + "<script>var a \nd</script>", + }, + { + "CSS comments", + "<style>p// paragraph\n" + + `{border: 1px/* color */{{"#00f"}}}</style>`, + "<style>p\n" + + "{border: 1px #00f}</style>", + }, + { + "JS attr block comment", + `<a onclick="f(""); /* alert({{.H}}) */">`, + // Attribute comment tests should pass if the comments + // are successfully elided. + `<a onclick="f(""); /* alert() */">`, + }, + { + "JS attr line comment", + `<a onclick="// alert({{.G}})">`, + `<a onclick="// alert()">`, + }, + { + "CSS attr block comment", + `<a style="/* color: {{.H}} */">`, + `<a style="/* color: */">`, + }, + { + "CSS attr line comment", + `<a style="// color: {{.G}}">`, + `<a style="// color: ">`, + }, + { + "HTML substitution commented out", + "<p><!-- {{.H}} --></p>", + "<p></p>", + }, + { + "Comment ends flush with start", + "<!--{{.}}--><script>/*{{.}}*///{{.}}\n</script><style>/*{{.}}*///{{.}}\n</style><a onclick='/*{{.}}*///{{.}}' style='/*{{.}}*///{{.}}'>", + "<script> \n</script><style> \n</style><a onclick='/**///' style='/**///'>", + }, + { + "typed HTML in text", + `{{.W}}`, + `¡<b class="foo">Hello</b>, <textarea>O'World</textarea>!`, + }, + { + "typed HTML in attribute", + `<div title="{{.W}}">`, + `<div title="¡Hello, O'World!">`, + }, + { + "typed HTML in script", + `<button onclick="alert({{.W}})">`, + `<button onclick="alert("&iexcl;\u003cb class=\"foo\"\u003eHello\u003c/b\u003e, \u003ctextarea\u003eO'World\u003c/textarea\u003e!")">`, + }, + { + "typed HTML in RCDATA", + `<textarea>{{.W}}</textarea>`, + `<textarea>¡<b class="foo">Hello</b>, <textarea>O'World</textarea>!</textarea>`, + }, + { + "range in textarea", + "<textarea>{{range .A}}{{.}}{{end}}</textarea>", + "<textarea><a><b></textarea>", + }, + { + "auditable exemption from escaping", + "{{range .A}}{{. | noescape}}{{end}}", + "<a><b>", + }, + { + "No tag injection", + `{{"10$"}}<{{"script src,evil.org/pwnd.js"}}...`, + `10$<script src,evil.org/pwnd.js...`, + }, + { + "No comment injection", + `<{{"!--"}}`, + `<!--`, + }, + { + "No RCDATA end tag injection", + `<textarea><{{"/textarea "}}...</textarea>`, + `<textarea></textarea ...</textarea>`, + }, + { + "optional attrs", + `<img class="{{"iconClass"}}"` + + `{{if .T}} id="{{"<iconId>"}}"{{end}}` + + // Double quotes inside if/else. + ` src=` + + `{{if .T}}"?{{"<iconPath>"}}"` + + `{{else}}"images/cleardot.gif"{{end}}` + + // Missing space before title, but it is not a + // part of the src attribute. + `{{if .T}}title="{{"<title>"}}"{{end}}` + + // Quotes outside if/else. + ` alt="` + + `{{if .T}}{{"<alt>"}}` + + `{{else}}{{if .F}}{{"<title>"}}{{end}}` + + `{{end}}"` + + `>`, + `<img class="iconClass" id="<iconId>" src="?%3ciconPath%3e"title="<title>" alt="<alt>">`, + }, + { + "conditional valueless attr name", + `<input{{if .T}} checked{{end}} name=n>`, + `<input checked name=n>`, + }, + { + "conditional dynamic valueless attr name 1", + `<input{{if .T}} {{"checked"}}{{end}} name=n>`, + `<input checked name=n>`, + }, + { + "conditional dynamic valueless attr name 2", + `<input {{if .T}}{{"checked"}} {{end}}name=n>`, + `<input checked name=n>`, + }, + { + "dynamic attribute name", + `<img on{{"load"}}="alert({{"loaded"}})">`, + // Treated as JS since quotes are inserted. + `<img onload="alert("loaded")">`, + }, + { + "bad dynamic attribute name 1", + // Allow checked, selected, disabled, but not JS or + // CSS attributes. + `<input {{"onchange"}}="{{"doEvil()"}}">`, + `<input ZgotmplZ="doEvil()">`, + }, + { + "bad dynamic attribute name 2", + `<div {{"sTyle"}}="{{"color: expression(alert(1337))"}}">`, + `<div ZgotmplZ="color: expression(alert(1337))">`, + }, + { + "bad dynamic attribute name 3", + // Allow title or alt, but not a URL. + `<img {{"src"}}="{{"javascript:doEvil()"}}">`, + `<img ZgotmplZ="javascript:doEvil()">`, + }, + { + "bad dynamic attribute name 4", + // Structure preservation requires values to associate + // with a consistent attribute. + `<input checked {{""}}="Whose value am I?">`, + `<input checked ZgotmplZ="Whose value am I?">`, + }, + { + "dynamic element name", + `<h{{3}}><table><t{{"head"}}>...</h{{3}}>`, + `<h3><table><thead>...</h3>`, + }, + { + "bad dynamic element name", + // Dynamic element names are typically used to switch + // between (thead, tfoot, tbody), (ul, ol), (th, td), + // and other replaceable sets. + // We do not currently easily support (ul, ol). + // If we do change to support that, this test should + // catch failures to filter out special tag names which + // would violate the structure preservation property -- + // if any special tag name could be substituted, then + // the content could be raw text/RCDATA for some inputs + // and regular HTML content for others. + `<{{"script"}}>{{"doEvil()"}}</{{"script"}}>`, + `<script>doEvil()</script>`, + }, + } + + for _, test := range tests { + tmpl := template.New(test.name) + // TODO: Move noescape into template/func.go + tmpl.Funcs(template.FuncMap{ + "noescape": func(a ...interface{}) string { + return fmt.Sprint(a...) + }, + }) + tmpl = template.Must(Escape(template.Must(tmpl.Parse(test.input)))) + b := new(bytes.Buffer) + if err := tmpl.Execute(b, data); err != nil { + t.Errorf("%s: template execution failed: %s", test.name, err) + continue + } + if w, g := test.output, b.String(); w != g { + t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g) + continue + } + } } -var testCases = []testCase{ - {"if", "{{if .T}}Hello{{end}}, {{.C}}!", "Hello, <Cincinatti>!"}, - {"else", "{{if .F}}{{.H}}{{else}}{{.G}}{{end}}!", "<Goodbye>!"}, - {"overescaping", "Hello, {{.C | html}}!", "Hello, <Cincinatti>!"}, - {"assignment", "{{if $x := .H}}{{$x}}{{end}}", "<Hello>"}, - {"withBody", "{{with .H}}{{.}}{{end}}", "<Hello>"}, - {"withElse", "{{with .E}}{{.}}{{else}}{{.H}}{{end}}", "<Hello>"}, - {"rangeBody", "{{range .A}}{{.}}{{end}}", "<a><b>"}, - {"rangeElse", "{{range .E}}{{.}}{{else}}{{.H}}{{end}}", "<Hello>"}, - {"nonStringValue", "{{.T}}", "true"}, - {"constant", `<a href="{{"'str'"}}">`, `<a href="'str'">`}, +func TestEscapeSet(t *testing.T) { + type dataItem struct { + Children []*dataItem + X string + } + + data := dataItem{ + Children: []*dataItem{ + &dataItem{X: "foo"}, + &dataItem{X: "<bar>"}, + &dataItem{ + Children: []*dataItem{ + &dataItem{X: "baz"}, + }, + }, + }, + } + + tests := []struct { + inputs map[string]string + want string + }{ + // The trivial set. + { + map[string]string{ + "main": ``, + }, + ``, + }, + // A template called in the start context. + { + map[string]string{ + "main": `Hello, {{template "helper"}}!`, + // Not a valid top level HTML template. + // "<b" is not a full tag. + "helper": `{{"<World>"}}`, + }, + `Hello, <World>!`, + }, + // A template called in a context other than the start. + { + map[string]string{ + "main": `<a onclick='a = {{template "helper"}};'>`, + // Not a valid top level HTML template. + // "<b" is not a full tag. + "helper": `{{"<a>"}}<b`, + }, + `<a onclick='a = "\u003ca\u003e"<b;'>`, + }, + // A recursive template that ends in its start context. + { + map[string]string{ + "main": `{{range .Children}}{{template "main" .}}{{else}}{{.X}} {{end}}`, + }, + `foo <bar> baz `, + }, + // A recursive helper template that ends in its start context. + { + map[string]string{ + "main": `{{template "helper" .}}`, + "helper": `{{if .Children}}<ul>{{range .Children}}<li>{{template "main" .}}</li>{{end}}</ul>{{else}}{{.X}}{{end}}`, + }, + `<ul><li>foo</li><li><bar></li><li><ul><li>baz</li></ul></li></ul>`, + }, + // Co-recursive templates that end in its start context. + { + map[string]string{ + "main": `<blockquote>{{range .Children}}{{template "helper" .}}{{end}}</blockquote>`, + "helper": `{{if .Children}}{{template "main" .}}{{else}}{{.X}}<br>{{end}}`, + }, + `<blockquote>foo<br><bar><br><blockquote>baz<br></blockquote></blockquote>`, + }, + // A template that is called in two different contexts. + { + map[string]string{ + "main": `<button onclick="title='{{template "helper"}}'; ...">{{template "helper"}}</button>`, + "helper": `{{11}} of {{"<100>"}}`, + }, + `<button onclick="title='11 of \x3c100\x3e'; ...">11 of <100></button>`, + }, + // A non-recursive template that ends in a different context. + // helper starts in jsCtxRegexp and ends in jsCtxDivOp. + { + map[string]string{ + "main": `<script>var x={{template "helper"}}/{{"42"}};</script>`, + "helper": "{{126}}", + }, + `<script>var x= 126 /"42";</script>`, + }, + // A recursive template that ends in a similar context. + { + map[string]string{ + "main": `<script>var x=[{{template "countdown" 4}}];</script>`, + "countdown": `{{.}}{{if .}},{{template "countdown" . | pred}}{{end}}`, + }, + `<script>var x=[ 4 , 3 , 2 , 1 , 0 ];</script>`, + }, + // A recursive template that ends in a different context. + /* + { + map[string]string{ + "main": `<a href="/foo{{template "helper" .}}">`, + "helper": `{{if .Children}}{{range .Children}}{{template "helper" .}}{{end}}{{else}}?x={{.X}}{{end}}`, + }, + `<a href="/foo?x=foo?x=%3cbar%3e?x=baz">`, + }, + */ + } + + // pred is a template function that returns the predecessor of a + // natural number for testing recursive templates. + fns := template.FuncMap{"pred": func(a ...interface{}) (interface{}, os.Error) { + if len(a) == 1 { + if i, _ := a[0].(int); i > 0 { + return i - 1, nil + } + } + return nil, fmt.Errorf("undefined pred(%v)", a) + }} + + for _, test := range tests { + var s template.Set + for name, src := range test.inputs { + t := template.New(name) + t.Funcs(fns) + s.Add(template.Must(t.Parse(src))) + } + s.Funcs(fns) + if _, err := EscapeSet(&s, "main"); err != nil { + t.Errorf("%s for input:\n%v", err, test.inputs) + continue + } + var b bytes.Buffer + + if err := s.Execute(&b, "main", data); err != nil { + t.Errorf("%q executing %v", err.String(), s.Template("main")) + continue + } + if got := b.String(); test.want != got { + t.Errorf("want\n\t%q\ngot\n\t%q", test.want, got) + } + } + } -func TestAutoesc(t *testing.T) { - for _, testCase := range testCases { - name := testCase.name - tmpl := template.New(name) - tmpl, err := tmpl.Parse(testCase.input) +func TestErrors(t *testing.T) { + tests := []struct { + input string + err string + }{ + // Non-error cases. + { + "{{if .Cond}}<a>{{else}}<b>{{end}}", + "", + }, + { + "{{if .Cond}}<a>{{end}}", + "", + }, + { + "{{if .Cond}}{{else}}<b>{{end}}", + "", + }, + { + "{{with .Cond}}<div>{{end}}", + "", + }, + { + "{{range .Items}}<a>{{end}}", + "", + }, + { + "<a href='/foo?{{range .Items}}&{{.K}}={{.V}}{{end}}'>", + "", + }, + // Error cases. + { + "{{if .Cond}}<a{{end}}", + "z:1: {{if}} branches", + }, + { + "{{if .Cond}}\n{{else}}\n<a{{end}}", + "z:1: {{if}} branches", + }, + { + // Missing quote in the else branch. + `{{if .Cond}}<a href="foo">{{else}}<a href="bar>{{end}}`, + "z:1: {{if}} branches", + }, + { + // Different kind of attribute: href implies a URL. + "<a {{if .Cond}}href='{{else}}title='{{end}}{{.X}}'>", + "z:1: {{if}} branches", + }, + { + "\n{{with .X}}<a{{end}}", + "z:2: {{with}} branches", + }, + { + "\n{{with .X}}<a>{{else}}<a{{end}}", + "z:2: {{with}} branches", + }, + { + "{{range .Items}}<a{{end}}", + `z:1: on range loop re-entry: "<" in attribute name: "<a"`, + }, + { + "\n{{range .Items}} x='<a{{end}}", + "z:2: on range loop re-entry: {{range}} branches", + }, + { + "<a b=1 c={{.H}}", + "z: ends in a non-text context: {stateAttr delimSpaceOrTagEnd", + }, + { + "<script>foo();", + "z: ends in a non-text context: {stateJS", + }, + { + `<a href="{{if .F}}/foo?a={{else}}/bar/{{end}}{{.H}}">`, + "z:1: (action: [(command: [F=[H]])]) appears in an ambiguous URL context", + }, + { + `<a onclick="alert('Hello \`, + `unfinished escape sequence in JS string: "Hello \\"`, + }, + { + `<a onclick='alert("Hello\, World\`, + `unfinished escape sequence in JS string: "Hello\\, World\\"`, + }, + { + `<a onclick='alert(/x+\`, + `unfinished escape sequence in JS string: "x+\\"`, + }, + { + `<a onclick="/foo[\]/`, + `unfinished JS regexp charset: "foo[\\]/"`, + }, + { + // It is ambiguous whether 1.5 should be 1\.5 or 1.5. + // Either `var x = 1/- 1.5 /i.test(x)` + // where `i.test(x)` is a method call of reference i, + // or `/-1\.5/i.test(x)` which is a method call on a + // case insensitive regular expression. + `<script>{{if false}}var x = 1{{end}}/-{{"1.5"}}/i.test(x)</script>`, + `'/' could start a division or regexp: "/-"`, + }, + { + `{{template "foo"}}`, + "z:1: no such template foo", + }, + { + `{{define "z"}}<div{{template "y"}}>{{end}}` + + // Illegal starting in stateTag but not in stateText. + `{{define "y"}} foo<b{{end}}`, + `"<" in attribute name: " foo<b"`, + }, + { + `{{define "z"}}<script>reverseList = [{{template "t"}}]</script>{{end}}` + + // Missing " after recursive call. + `{{define "t"}}{{if .Tail}}{{template "t" .Tail}}{{end}}{{.Head}}",{{end}}`, + `: cannot compute output context for template t$htmltemplate_stateJS_elementScript`, + }, + { + `<input type=button value=onclick=>`, + `exp/template/html:z: "=" in unquoted attr: "onclick="`, + }, + { + `<input type=button value= onclick=>`, + `exp/template/html:z: "=" in unquoted attr: "onclick="`, + }, + { + `<input type=button value= 1+1=2>`, + `exp/template/html:z: "=" in unquoted attr: "1+1=2"`, + }, + { + "<a class=`foo>", + "exp/template/html:z: \"`\" in unquoted attr: \"`foo\"", + }, + { + `<a style=font:'Arial'>`, + `exp/template/html:z: "'" in unquoted attr: "font:'Arial'"`, + }, + { + `<a=foo>`, + `: expected space, attr name, or end of tag, but got "=foo>"`, + }, + } + + for _, test := range tests { + var err os.Error + if strings.HasPrefix(test.input, "{{define") { + var s template.Set + _, err = s.Parse(test.input) + if err != nil { + t.Errorf("Failed to parse %q: %s", test.input, err) + continue + } + _, err = EscapeSet(&s, "z") + } else { + tmpl := template.Must(template.New("z").Parse(test.input)) + _, err = Escape(tmpl) + } + var got string if err != nil { - t.Errorf("%s: failed to parse template: %s", name, err) + got = err.String() + } + if test.err == "" { + if got != "" { + t.Errorf("input=%q: unexpected error %q", test.input, got) + } + continue + } + if strings.Index(got, test.err) == -1 { + t.Errorf("input=%q: error\n\t%q\ndoes not contain expected string\n\t%q", test.input, got, test.err) continue } + } +} - Escape(tmpl) +func TestEscapeText(t *testing.T) { + tests := []struct { + input string + output context + }{ + { + ``, + context{}, + }, + { + `Hello, World!`, + context{}, + }, + { + // An orphaned "<" is OK. + `I <3 Ponies!`, + context{}, + }, + { + `<a`, + context{state: stateTag}, + }, + { + `<a `, + context{state: stateTag}, + }, + { + `<a>`, + context{state: stateText}, + }, + { + `<a href`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a on`, + context{state: stateAttrName, attr: attrScript}, + }, + { + `<a href `, + context{state: stateAfterName, attr: attrURL}, + }, + { + `<a style = `, + context{state: stateBeforeValue, attr: attrStyle}, + }, + { + `<a href=`, + context{state: stateBeforeValue, attr: attrURL}, + }, + { + `<a href=x`, + context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery}, + }, + { + `<a href=x `, + context{state: stateTag}, + }, + { + `<a href=>`, + context{state: stateText}, + }, + { + `<a href=x>`, + context{state: stateText}, + }, + { + `<a href ='`, + context{state: stateURL, delim: delimSingleQuote}, + }, + { + `<a href=''`, + context{state: stateTag}, + }, + { + `<a href= "`, + context{state: stateURL, delim: delimDoubleQuote}, + }, + { + `<a href=""`, + context{state: stateTag}, + }, + { + `<a title="`, + context{state: stateAttr, delim: delimDoubleQuote}, + }, + { + `<a HREF='http:`, + context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a Href='/`, + context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a href='"`, + context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a href="'`, + context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a href=''`, + context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a href=""`, + context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a href=""`, + context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a href="`, + context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery}, + }, + { + `<img alt="1">`, + context{state: stateText}, + }, + { + `<img alt="1>"`, + context{state: stateTag}, + }, + { + `<img alt="1>">`, + context{state: stateText}, + }, + { + `<input checked type="checkbox"`, + context{state: stateTag}, + }, + { + `<a onclick="`, + context{state: stateJS, delim: delimDoubleQuote}, + }, + { + `<a onclick="//foo`, + context{state: stateJSLineCmt, delim: delimDoubleQuote}, + }, + { + "<a onclick='//\n", + context{state: stateJS, delim: delimSingleQuote}, + }, + { + "<a onclick='//\r\n", + context{state: stateJS, delim: delimSingleQuote}, + }, + { + "<a onclick='//\u2028", + context{state: stateJS, delim: delimSingleQuote}, + }, + { + `<a onclick="/*`, + context{state: stateJSBlockCmt, delim: delimDoubleQuote}, + }, + { + `<a onclick="/*/`, + context{state: stateJSBlockCmt, delim: delimDoubleQuote}, + }, + { + `<a onclick="/**/`, + context{state: stateJS, delim: delimDoubleQuote}, + }, + { + `<a onkeypress=""`, + context{state: stateJSDqStr, delim: delimDoubleQuote}, + }, + { + `<a onclick='"foo"`, + context{state: stateJS, delim: delimSingleQuote, jsCtx: jsCtxDivOp}, + }, + { + `<a onclick='foo'`, + context{state: stateJS, delim: delimSpaceOrTagEnd, jsCtx: jsCtxDivOp}, + }, + { + `<a onclick='foo`, + context{state: stateJSSqStr, delim: delimSpaceOrTagEnd}, + }, + { + `<a onclick=""foo'`, + context{state: stateJSDqStr, delim: delimDoubleQuote}, + }, + { + `<a onclick="'foo"`, + context{state: stateJSSqStr, delim: delimDoubleQuote}, + }, + { + `<A ONCLICK="'`, + context{state: stateJSSqStr, delim: delimDoubleQuote}, + }, + { + `<a onclick="/`, + context{state: stateJSRegexp, delim: delimDoubleQuote}, + }, + { + `<a onclick="'foo'`, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + }, + { + `<a onclick="'foo\'`, + context{state: stateJSSqStr, delim: delimDoubleQuote}, + }, + { + `<a onclick="'foo\'`, + context{state: stateJSSqStr, delim: delimDoubleQuote}, + }, + { + `<a onclick="/foo/`, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + }, + { + `<script>/foo/ /=`, + context{state: stateJS, element: elementScript}, + }, + { + `<a onclick="1 /foo`, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + }, + { + `<a onclick="1 /*c*/ /foo`, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + }, + { + `<a onclick="/foo[/]`, + context{state: stateJSRegexp, delim: delimDoubleQuote}, + }, + { + `<a onclick="/foo\/`, + context{state: stateJSRegexp, delim: delimDoubleQuote}, + }, + { + `<a onclick="/foo/`, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + }, + { + `<input checked style="`, + context{state: stateCSS, delim: delimDoubleQuote}, + }, + { + `<a style="//`, + context{state: stateCSSLineCmt, delim: delimDoubleQuote}, + }, + { + `<a style="//</script>`, + context{state: stateCSSLineCmt, delim: delimDoubleQuote}, + }, + { + "<a style='//\n", + context{state: stateCSS, delim: delimSingleQuote}, + }, + { + "<a style='//\r", + context{state: stateCSS, delim: delimSingleQuote}, + }, + { + `<a style="/*`, + context{state: stateCSSBlockCmt, delim: delimDoubleQuote}, + }, + { + `<a style="/*/`, + context{state: stateCSSBlockCmt, delim: delimDoubleQuote}, + }, + { + `<a style="/**/`, + context{state: stateCSS, delim: delimDoubleQuote}, + }, + { + `<a style="background: '`, + context{state: stateCSSSqStr, delim: delimDoubleQuote}, + }, + { + `<a style="background: "`, + context{state: stateCSSDqStr, delim: delimDoubleQuote}, + }, + { + `<a style="background: '/foo?img=`, + context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag}, + }, + { + `<a style="background: '/`, + context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url("/`, + context{state: stateCSSDqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url('/`, + context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url('/)`, + context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url('/ `, + context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url(/`, + context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url( `, + context{state: stateCSSURL, delim: delimDoubleQuote}, + }, + { + `<a style="background: url( /image?name=`, + context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag}, + }, + { + `<a style="background: url(x)`, + context{state: stateCSS, delim: delimDoubleQuote}, + }, + { + `<a style="background: url('x'`, + context{state: stateCSS, delim: delimDoubleQuote}, + }, + { + `<a style="background: url( x `, + context{state: stateCSS, delim: delimDoubleQuote}, + }, + { + `<!-- foo`, + context{state: stateHTMLCmt}, + }, + { + `<!-->`, + context{state: stateHTMLCmt}, + }, + { + `<!--->`, + context{state: stateHTMLCmt}, + }, + { + `<!-- foo -->`, + context{state: stateText}, + }, + { + `<script`, + context{state: stateTag, element: elementScript}, + }, + { + `<script `, + context{state: stateTag, element: elementScript}, + }, + { + `<script src="foo.js" `, + context{state: stateTag, element: elementScript}, + }, + { + `<script src='foo.js' `, + context{state: stateTag, element: elementScript}, + }, + { + `<script type=text/javascript `, + context{state: stateTag, element: elementScript}, + }, + { + `<script>foo`, + context{state: stateJS, jsCtx: jsCtxDivOp, element: elementScript}, + }, + { + `<script>foo</script>`, + context{state: stateText}, + }, + { + `<script>foo</script><!--`, + context{state: stateHTMLCmt}, + }, + { + `<script>document.write("<p>foo</p>");`, + context{state: stateJS, element: elementScript}, + }, + { + `<script>document.write("<p>foo<\/script>");`, + context{state: stateJS, element: elementScript}, + }, + { + `<script>document.write("<script>alert(1)</script>");`, + context{state: stateText}, + }, + { + `<Script>`, + context{state: stateJS, element: elementScript}, + }, + { + `<SCRIPT>foo`, + context{state: stateJS, jsCtx: jsCtxDivOp, element: elementScript}, + }, + { + `<textarea>value`, + context{state: stateRCDATA, element: elementTextarea}, + }, + { + `<textarea>value</TEXTAREA>`, + context{state: stateText}, + }, + { + `<textarea name=html><b`, + context{state: stateRCDATA, element: elementTextarea}, + }, + { + `<title>value`, + context{state: stateRCDATA, element: elementTitle}, + }, + { + `<style>value`, + context{state: stateCSS, element: elementStyle}, + }, + { + `<a xlink:href`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a xmlns`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a xmlns:foo`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a xmlnsxyz`, + context{state: stateAttrName}, + }, + { + `<a data-url`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a data-iconUri`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a data-urlItem`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a g:`, + context{state: stateAttrName}, + }, + { + `<a g:url`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a g:iconUri`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a g:urlItem`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a g:value`, + context{state: stateAttrName}, + }, + { + `<a svg:style='`, + context{state: stateCSS, delim: delimSingleQuote}, + }, + { + `<svg:font-face`, + context{state: stateTag}, + }, + { + `<svg:a svg:onclick="`, + context{state: stateJS, delim: delimDoubleQuote}, + }, + } - buffer := new(bytes.Buffer) + for _, test := range tests { + b, e := []byte(test.input), newEscaper(nil) + c := e.escapeText(context{}, &parse.TextNode{parse.NodeText, b}) + if !test.output.eq(c) { + t.Errorf("input %q: want context\n\t%v\ngot\n\t%v", test.input, test.output, c) + continue + } + if test.input != string(b) { + t.Errorf("input %q: text node was modified: want %q got %q", test.input, test.input, b) + continue + } + } +} - err = tmpl.Execute(buffer, testData) - if err != nil { - t.Errorf("%s: template execution failed: %s", name, err) +func TestEnsurePipelineContains(t *testing.T) { + tests := []struct { + input, output string + ids []string + }{ + { + "{{.X}}", + "[(command: [F=[X]])]", + []string{}, + }, + { + "{{.X | html}}", + "[(command: [F=[X]]) (command: [I=html])]", + []string{}, + }, + { + "{{.X}}", + "[(command: [F=[X]]) (command: [I=html])]", + []string{"html"}, + }, + { + "{{.X | html}}", + "[(command: [F=[X]]) (command: [I=html]) (command: [I=urlquery])]", + []string{"urlquery"}, + }, + { + "{{.X | html | urlquery}}", + "[(command: [F=[X]]) (command: [I=html]) (command: [I=urlquery])]", + []string{"urlquery"}, + }, + { + "{{.X | html | urlquery}}", + "[(command: [F=[X]]) (command: [I=html]) (command: [I=urlquery])]", + []string{"html", "urlquery"}, + }, + { + "{{.X | html | urlquery}}", + "[(command: [F=[X]]) (command: [I=html]) (command: [I=urlquery])]", + []string{"html"}, + }, + { + "{{.X | urlquery}}", + "[(command: [F=[X]]) (command: [I=html]) (command: [I=urlquery])]", + []string{"html", "urlquery"}, + }, + { + "{{.X | html | print}}", + "[(command: [F=[X]]) (command: [I=urlquery]) (command: [I=html]) (command: [I=print])]", + []string{"urlquery", "html"}, + }, + } + for _, test := range tests { + tmpl := template.Must(template.New("test").Parse(test.input)) + action, ok := (tmpl.Tree.Root.Nodes[0].(*parse.ActionNode)) + if !ok { + t.Errorf("First node is not an action: %s", test.input) continue } + pipe := action.Pipe + ensurePipelineContains(pipe, test.ids) + got := pipe.String() + if got != test.output { + t.Errorf("%s, %v: want\n\t%s\ngot\n\t%s", test.input, test.ids, test.output, got) + } + } +} - output := testCase.output - actual := buffer.String() - if output != actual { - t.Errorf("%s: escaped output: %q != %q", - name, output, actual) +func expectExecuteFailure(t *testing.T, b *bytes.Buffer) { + if x := recover(); x != nil { + if b.Len() != 0 { + t.Errorf("output on buffer: %q", b.String()) } + } else { + t.Errorf("unescaped template executed") + } +} + +func TestEscapeErrorsNotIgnorable(t *testing.T) { + var b bytes.Buffer + tmpl := template.Must(template.New("dangerous").Parse("<a")) + Escape(tmpl) + defer expectExecuteFailure(t, &b) + tmpl.Execute(&b, nil) +} + +func TestEscapeSetErrorsNotIgnorable(t *testing.T) { + s, err := (&template.Set{}).Parse(`{{define "t"}}<a{{end}}`) + if err != nil { + t.Errorf("failed to parse set: %q", err) + } + EscapeSet(s, "t") + var b bytes.Buffer + defer expectExecuteFailure(t, &b) + s.Execute(&b, "t", nil) +} + +func TestRedundantFuncs(t *testing.T) { + inputs := []interface{}{ + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" + + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !"#$%&'()*+,-./` + + `0123456789:;<=>?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\]^_` + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\x7f" + + "\u00A0\u0100\u2028\u2029\ufeff\ufdec\ufffd\uffff\U0001D11E" + + "&%22\\", + CSS(`a[href =~ "//example.com"]#foo`), + HTML(`Hello, <b>World</b> &tc!`), + HTMLAttr(` dir="ltr"`), + JS(`c && alert("Hello, World!");`), + JSStr(`Hello, World & O'Reilly\x21`), + URL(`greeting=H%69&addressee=(World)`), + } + + for n0, m := range redundantFuncs { + f0 := funcMap[n0].(func(...interface{}) string) + for n1, _ := range m { + f1 := funcMap[n1].(func(...interface{}) string) + for _, input := range inputs { + want := f0(input) + if got := f1(want); want != got { + t.Errorf("%s %s with %T %q: want\n\t%q,\ngot\n\t%q", n0, n1, input, input, want, got) + } + } + } + } +} + +func BenchmarkEscapedExecute(b *testing.B) { + tmpl := template.Must(Escape(template.Must(template.New("t").Parse(`<a onclick="alert('{{.}}')">{{.}}</a>`)))) + var buf bytes.Buffer + b.ResetTimer() + for i := 0; i < b.N; i++ { + tmpl.Execute(&buf, "foo & 'bar' & baz") + buf.Reset() } } diff --git a/libgo/go/exp/template/html/html.go b/libgo/go/exp/template/html/html.go new file mode 100644 index 00000000000..91bb1b17049 --- /dev/null +++ b/libgo/go/exp/template/html/html.go @@ -0,0 +1,257 @@ +// 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 html + +import ( + "bytes" + "fmt" + "strings" + "utf8" +) + +// htmlNospaceEscaper escapes for inclusion in unquoted attribute values. +func htmlNospaceEscaper(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeHTML { + return htmlReplacer(stripTags(s), htmlNospaceNormReplacementTable, false) + } + return htmlReplacer(s, htmlNospaceReplacementTable, false) +} + +// attrEscaper escapes for inclusion in quoted attribute values. +func attrEscaper(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeHTML { + return htmlReplacer(stripTags(s), htmlNormReplacementTable, true) + } + return htmlReplacer(s, htmlReplacementTable, true) +} + +// rcdataEscaper escapes for inclusion in an RCDATA element body. +func rcdataEscaper(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeHTML { + return htmlReplacer(s, htmlNormReplacementTable, true) + } + return htmlReplacer(s, htmlReplacementTable, true) +} + +// htmlEscaper escapes for inclusion in HTML text. +func htmlEscaper(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeHTML { + return s + } + return htmlReplacer(s, htmlReplacementTable, true) +} + +// htmlReplacementTable contains the runes that need to be escaped +// inside a quoted attribute value or in a text node. +var htmlReplacementTable = []string{ + // http://www.w3.org/TR/html5/tokenization.html#attribute-value-unquoted-state: " + // U+0000 NULL Parse error. Append a U+FFFD REPLACEMENT + // CHARACTER character to the current attribute's value. + // " + // and similarly + // http://www.w3.org/TR/html5/tokenization.html#before-attribute-value-state + 0: "\uFFFD", + '"': """, + '&': "&", + '\'': "'", + '+': "+", + '<': "<", + '>': ">", +} + +// htmlNormReplacementTable is like htmlReplacementTable but without '&' to +// avoid over-encoding existing entities. +var htmlNormReplacementTable = []string{ + 0: "\uFFFD", + '"': """, + '\'': "'", + '+': "+", + '<': "<", + '>': ">", +} + +// htmlNospaceReplacementTable contains the runes that need to be escaped +// inside an unquoted attribute value. +// The set of runes escaped is the union of the HTML specials and +// those determined by running the JS below in browsers: +// <div id=d></div> +// <script>(function () { +// var a = [], d = document.getElementById("d"), i, c, s; +// for (i = 0; i < 0x10000; ++i) { +// c = String.fromCharCode(i); +// d.innerHTML = "<span title=" + c + "lt" + c + "></span>" +// s = d.getElementsByTagName("SPAN")[0]; +// if (!s || s.title !== c + "lt" + c) { a.push(i.toString(16)); } +// } +// document.write(a.join(", ")); +// })()</script> +var htmlNospaceReplacementTable = []string{ + 0: "�", + '\t': "	", + '\n': " ", + '\v': "", + '\f': "", + '\r': " ", + ' ': " ", + '"': """, + '&': "&", + '\'': "'", + '+': "+", + '<': "<", + '=': "=", + '>': ">", + // A parse error in the attribute value (unquoted) and + // before attribute value states. + // Treated as a quoting character by IE. + '`': "`", +} + +// htmlNospaceNormReplacementTable is like htmlNospaceReplacementTable but +// without '&' to avoid over-encoding existing entities. +var htmlNospaceNormReplacementTable = []string{ + 0: "�", + '\t': "	", + '\n': " ", + '\v': "", + '\f': "", + '\r': " ", + ' ': " ", + '"': """, + '\'': "'", + '+': "+", + '<': "<", + '=': "=", + '>': ">", + // A parse error in the attribute value (unquoted) and + // before attribute value states. + // Treated as a quoting character by IE. + '`': "`", +} + +// htmlReplacer returns s with runes replaced acccording to replacementTable +// and when badRunes is true, certain bad runes are allowed through unescaped. +func htmlReplacer(s string, replacementTable []string, badRunes bool) string { + written, b := 0, new(bytes.Buffer) + for i, r := range s { + if r < len(replacementTable) { + if repl := replacementTable[r]; len(repl) != 0 { + b.WriteString(s[written:i]) + b.WriteString(repl) + // Valid as long as replacementTable doesn't + // include anything above 0x7f. + written = i + utf8.RuneLen(r) + } + } else if badRunes { + // No-op. + // IE does not allow these ranges in unquoted attrs. + } else if 0xfdd0 <= r && r <= 0xfdef || 0xfff0 <= r && r <= 0xffff { + fmt.Fprintf(b, "%s&#x%x;", s[written:i], r) + written = i + utf8.RuneLen(r) + } + } + if written == 0 { + return s + } + b.WriteString(s[written:]) + return b.String() +} + +// stripTags takes a snippet of HTML and returns only the text content. +// For example, `<b>¡Hi!</b> <script>...</script>` -> `¡Hi! `. +func stripTags(html string) string { + var b bytes.Buffer + s, c, i, allText := []byte(html), context{}, 0, true + // Using the transition funcs helps us avoid mangling + // `<div title="1>2">` or `I <3 Ponies!`. + for i != len(s) { + if c.delim == delimNone { + st := c.state + // Use RCDATA instead of parsing into JS or CSS styles. + if c.element != elementNone && !isInTag(st) { + st = stateRCDATA + } + d, nread := transitionFunc[st](c, s[i:]) + i1 := i + nread + if c.state == stateText || c.state == stateRCDATA { + // Emit text up to the start of the tag or comment. + j := i1 + if d.state != c.state { + for j1 := j - 1; j1 >= i; j1-- { + if s[j1] == '<' { + j = j1 + break + } + } + } + b.Write(s[i:j]) + } else { + allText = false + } + c, i = d, i1 + continue + } + i1 := i + bytes.IndexAny(s[i:], delimEnds[c.delim]) + if i1 < i { + break + } + if c.delim != delimSpaceOrTagEnd { + // Consume any quote. + i1++ + } + c, i = context{state: stateTag, element: c.element}, i1 + } + if allText { + return html + } else if c.state == stateText || c.state == stateRCDATA { + b.Write(s[i:]) + } + return b.String() +} + +// htmlNameFilter accepts valid parts of an HTML attribute or tag name or +// a known-safe HTML attribute. +func htmlNameFilter(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeHTMLAttr { + return s + } + if len(s) == 0 { + // Avoid violation of structure preservation. + // <input checked {{.K}}={{.V}}>. + // Without this, if .K is empty then .V is the value of + // checked, but otherwise .V is the value of the attribute + // named .K. + return filterFailsafe + } + s = strings.ToLower(s) + if t := attrType(s); t != contentTypePlain { + // TODO: Split attr and element name part filters so we can whitelist + // attributes. + return filterFailsafe + } + for _, r := range s { + switch { + case '0' <= r && r <= '9': + case 'a' <= r && r <= 'z': + default: + return filterFailsafe + } + } + return s +} + +// commentEscaper returns the empty string regardless of input. +// Comment content does not correspond to any parsed structure or +// human-readable content, so the simplest and most secure policy is to drop +// content interpolated into comments. +// This approach is equally valid whether or not static comment content is +// removed from the template. +func commentEscaper(args ...interface{}) string { + return "" +} diff --git a/libgo/go/exp/template/html/html_test.go b/libgo/go/exp/template/html/html_test.go new file mode 100644 index 00000000000..e178d0f27e5 --- /dev/null +++ b/libgo/go/exp/template/html/html_test.go @@ -0,0 +1,94 @@ +// 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 html + +import ( + "html" + "strings" + "testing" +) + +func TestHTMLNospaceEscaper(t *testing.T) { + input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" + + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !"#$%&'()*+,-./` + + `0123456789:;<=>?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\]^_` + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\x7f" + + "\u00A0\u0100\u2028\u2029\ufeff\ufdec\U0001D11E") + + want := ("�\x01\x02\x03\x04\x05\x06\x07" + + "\x08	  \x0E\x0F" + + "\x10\x11\x12\x13\x14\x15\x16\x17" + + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !"#$%&'()*+,-./` + + `0123456789:;<=>?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\]^_` + + ``abcdefghijklmno` + + `pqrstuvwxyz{|}~` + "\u007f" + + "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") + + got := htmlNospaceEscaper(input) + if got != want { + t.Errorf("encode: want\n\t%q\nbut got\n\t%q", want, got) + } + + got, want = html.UnescapeString(got), strings.Replace(input, "\x00", "\ufffd", 1) + if want != got { + t.Errorf("decode: want\n\t%q\nbut got\n\t%q", want, got) + } +} + +func TestStripTags(t *testing.T) { + tests := []struct { + input, want string + }{ + {"", ""}, + {"Hello, World!", "Hello, World!"}, + {"foo&bar", "foo&bar"}, + {`Hello <a href="www.example.com/">World</a>!`, "Hello World!"}, + {"Foo <textarea>Bar</textarea> Baz", "Foo Bar Baz"}, + {"Foo <!-- Bar --> Baz", "Foo Baz"}, + {"<", "<"}, + {"foo < bar", "foo < bar"}, + {`Foo<script type="text/javascript">alert(1337)</script>Bar`, "FooBar"}, + {`Foo<div title="1>2">Bar`, "FooBar"}, + {`I <3 Ponies!`, `I <3 Ponies!`}, + {`<script>foo()</script>`, ``}, + } + + for _, test := range tests { + if got := stripTags(test.input); got != test.want { + t.Errorf("%q: want %q, got %q", test.input, test.want, got) + } + } +} + +func BenchmarkHTMLNospaceEscaper(b *testing.B) { + for i := 0; i < b.N; i++ { + htmlNospaceEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>") + } +} + +func BenchmarkHTMLNospaceEscaperNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + htmlNospaceEscaper("The_quick,_brown_fox_jumps_over_the_lazy_dog.") + } +} + +func BenchmarkStripTags(b *testing.B) { + for i := 0; i < b.N; i++ { + stripTags("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>") + } +} + +func BenchmarkStripTagsNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + stripTags("The quick, brown fox jumps over the lazy dog.") + } +} diff --git a/libgo/go/exp/template/html/js.go b/libgo/go/exp/template/html/js.go new file mode 100644 index 00000000000..98c2ac5f27f --- /dev/null +++ b/libgo/go/exp/template/html/js.go @@ -0,0 +1,346 @@ +// 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 html + +import ( + "bytes" + "fmt" + "json" + "strings" + "utf8" +) + +// nextJSCtx returns the context that determines whether a slash after the +// given run of tokens tokens starts a regular expression instead of a division +// operator: / or /=. +// +// This assumes that the token run does not include any string tokens, comment +// tokens, regular expression literal tokens, or division operators. +// +// This fails on some valid but nonsensical JavaScript programs like +// "x = ++/foo/i" which is quite different than "x++/foo/i", but is not known to +// fail on any known useful programs. It is based on the draft +// JavaScript 2.0 lexical grammar and requires one token of lookbehind: +// http://www.mozilla.org/js/language/js20-2000-07/rationale/syntax.html +func nextJSCtx(s []byte, preceding jsCtx) jsCtx { + s = bytes.TrimRight(s, "\t\n\f\r \u2028\u2029") + if len(s) == 0 { + return preceding + } + + // All cases below are in the single-byte UTF-8 group. + switch c, n := s[len(s)-1], len(s); c { + case '+', '-': + // ++ and -- are not regexp preceders, but + and - are whether + // they are used as infix or prefix operators. + start := n - 1 + // Count the number of adjacent dashes or pluses. + for start > 0 && s[start-1] == c { + start-- + } + if (n-start)&1 == 1 { + // Reached for trailing minus signs since "---" is the + // same as "-- -". + return jsCtxRegexp + } + return jsCtxDivOp + case '.': + // Handle "42." + if n != 1 && '0' <= s[n-2] && s[n-2] <= '9' { + return jsCtxDivOp + } + return jsCtxRegexp + // Suffixes for all punctuators from section 7.7 of the language spec + // that only end binary operators not handled above. + case ',', '<', '>', '=', '*', '%', '&', '|', '^', '?': + return jsCtxRegexp + // Suffixes for all punctuators from section 7.7 of the language spec + // that are prefix operators not handled above. + case '!', '~': + return jsCtxRegexp + // Matches all the punctuators from section 7.7 of the language spec + // that are open brackets not handled above. + case '(', '[': + return jsCtxRegexp + // Matches all the punctuators from section 7.7 of the language spec + // that precede expression starts. + case ':', ';', '{': + return jsCtxRegexp + // CAVEAT: the close punctuators ('}', ']', ')') precede div ops and + // are handled in the default except for '}' which can precede a + // division op as in + // ({ valueOf: function () { return 42 } } / 2 + // which is valid, but, in practice, developers don't divide object + // literals, so our heuristic works well for code like + // function () { ... } /foo/.test(x) && sideEffect(); + // The ')' punctuator can precede a regular expression as in + // if (b) /foo/.test(x) && ... + // but this is much less likely than + // (a + b) / c + case '}': + return jsCtxRegexp + default: + // Look for an IdentifierName and see if it is a keyword that + // can precede a regular expression. + j := n + for j > 0 && isJSIdentPart(int(s[j-1])) { + j-- + } + if regexpPrecederKeywords[string(s[j:])] { + return jsCtxRegexp + } + } + // Otherwise is a punctuator not listed above, or + // a string which precedes a div op, or an identifier + // which precedes a div op. + return jsCtxDivOp +} + +// regexPrecederKeywords is a set of reserved JS keywords that can precede a +// regular expression in JS source. +var regexpPrecederKeywords = map[string]bool{ + "break": true, + "case": true, + "continue": true, + "delete": true, + "do": true, + "else": true, + "finally": true, + "in": true, + "instanceof": true, + "return": true, + "throw": true, + "try": true, + "typeof": true, + "void": true, +} + +// jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has +// nether side-effects nor free variables outside (NaN, Infinity). +func jsValEscaper(args ...interface{}) string { + var a interface{} + if len(args) == 1 { + a = args[0] + switch t := a.(type) { + case JS: + return string(t) + case JSStr: + // TODO: normalize quotes. + return `"` + string(t) + `"` + case json.Marshaler: + // Do not treat as a Stringer. + case fmt.Stringer: + a = t.String() + } + } else { + a = fmt.Sprint(args...) + } + // TODO: detect cycles before calling Marshal which loops infinitely on + // cyclic data. This may be an unnacceptable DoS risk. + + b, err := json.Marshal(a) + if err != nil { + // Put a space before comment so that if it is flush against + // a division operator it is not turned into a line comment: + // x/{{y}} + // turning into + // x//* error marshalling y: + // second line of error message */null + return fmt.Sprintf(" /* %s */null ", strings.Replace(err.String(), "*/", "* /", -1)) + } + + // TODO: maybe post-process output to prevent it from containing + // "<!--", "-->", "<![CDATA[", "]]>", or "</script" + // in case custom marshallers produce output containing those. + + // TODO: Maybe abbreviate \u00ab to \xab to produce more compact output. + if len(b) == 0 { + // In, `x=y/{{.}}*z` a json.Marshaler that produces "" should + // not cause the output `x=y/*z`. + return " null " + } + first, _ := utf8.DecodeRune(b) + last, _ := utf8.DecodeLastRune(b) + var buf bytes.Buffer + // Prevent IdentifierNames and NumericLiterals from running into + // keywords: in, instanceof, typeof, void + pad := isJSIdentPart(first) || isJSIdentPart(last) + if pad { + buf.WriteByte(' ') + } + written := 0 + // Make sure that json.Marshal escapes codepoints U+2028 & U+2029 + // so it falls within the subset of JSON which is valid JS. + for i := 0; i < len(b); { + rune, n := utf8.DecodeRune(b[i:]) + repl := "" + if rune == 0x2028 { + repl = `\u2028` + } else if rune == 0x2029 { + repl = `\u2029` + } + if repl != "" { + buf.Write(b[written:i]) + buf.WriteString(repl) + written = i + n + } + i += n + } + if buf.Len() != 0 { + buf.Write(b[written:]) + if pad { + buf.WriteByte(' ') + } + b = buf.Bytes() + } + return string(b) +} + +// jsStrEscaper produces a string that can be included between quotes in +// JavaScript source, in JavaScript embedded in an HTML5 <script> element, +// or in an HTML5 event handler attribute such as onclick. +func jsStrEscaper(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeJSStr { + return replace(s, jsStrNormReplacementTable) + } + return replace(s, jsStrReplacementTable) +} + +// jsRegexpEscaper behaves like jsStrEscaper but escapes regular expression +// specials so the result is treated literally when included in a regular +// expression literal. /foo{{.X}}bar/ matches the string "foo" followed by +// the literal text of {{.X}} followed by the string "bar". +func jsRegexpEscaper(args ...interface{}) string { + s, _ := stringify(args...) + s = replace(s, jsRegexpReplacementTable) + if s == "" { + // /{{.X}}/ should not produce a line comment when .X == "". + return "(?:)" + } + return s +} + +// replace replaces each rune r of s with replacementTable[r], provided that +// r < len(replacementTable). If replacementTable[r] is the empty string then +// no replacement is made. +// It also replaces runes U+2028 and U+2029 with the raw strings `\u2028` and +// `\u2029`. +func replace(s string, replacementTable []string) string { + var b bytes.Buffer + written := 0 + for i, r := range s { + var repl string + switch { + case r < len(replacementTable) && replacementTable[r] != "": + repl = replacementTable[r] + case r == '\u2028': + repl = `\u2028` + case r == '\u2029': + repl = `\u2029` + default: + continue + } + b.WriteString(s[written:i]) + b.WriteString(repl) + written = i + utf8.RuneLen(r) + } + if written == 0 { + return s + } + b.WriteString(s[written:]) + return b.String() +} + +var jsStrReplacementTable = []string{ + 0: `\0`, + '\t': `\t`, + '\n': `\n`, + '\v': `\x0b`, // "\v" == "v" on IE 6. + '\f': `\f`, + '\r': `\r`, + // Encode HTML specials as hex so the output can be embedded + // in HTML attributes without further encoding. + '"': `\x22`, + '&': `\x26`, + '\'': `\x27`, + '+': `\x2b`, + '/': `\/`, + '<': `\x3c`, + '>': `\x3e`, + '\\': `\\`, +} + +// jsStrNormReplacementTable is like jsStrReplacementTable but does not +// overencode existing escapes since this table has no entry for `\`. +var jsStrNormReplacementTable = []string{ + 0: `\0`, + '\t': `\t`, + '\n': `\n`, + '\v': `\x0b`, // "\v" == "v" on IE 6. + '\f': `\f`, + '\r': `\r`, + // Encode HTML specials as hex so the output can be embedded + // in HTML attributes without further encoding. + '"': `\x22`, + '&': `\x26`, + '\'': `\x27`, + '+': `\x2b`, + '/': `\/`, + '<': `\x3c`, + '>': `\x3e`, +} + +var jsRegexpReplacementTable = []string{ + 0: `\0`, + '\t': `\t`, + '\n': `\n`, + '\v': `\x0b`, // "\v" == "v" on IE 6. + '\f': `\f`, + '\r': `\r`, + // Encode HTML specials as hex so the output can be embedded + // in HTML attributes without further encoding. + '"': `\x22`, + '$': `\$`, + '&': `\x26`, + '\'': `\x27`, + '(': `\(`, + ')': `\)`, + '*': `\*`, + '+': `\x2b`, + '-': `\-`, + '.': `\.`, + '/': `\/`, + '<': `\x3c`, + '>': `\x3e`, + '?': `\?`, + '[': `\[`, + '\\': `\\`, + ']': `\]`, + '^': `\^`, + '{': `\{`, + '|': `\|`, + '}': `\}`, +} + +// isJSIdentPart returns whether the given rune is a JS identifier part. +// It does not handle all the non-Latin letters, joiners, and combining marks, +// but it does handle every codepoint that can occur in a numeric literal or +// a keyword. +func isJSIdentPart(rune int) bool { + switch { + case '$' == rune: + return true + case '0' <= rune && rune <= '9': + return true + case 'A' <= rune && rune <= 'Z': + return true + case '_' == rune: + return true + case 'a' <= rune && rune <= 'z': + return true + } + return false +} diff --git a/libgo/go/exp/template/html/js_test.go b/libgo/go/exp/template/html/js_test.go new file mode 100644 index 00000000000..e7764054a35 --- /dev/null +++ b/libgo/go/exp/template/html/js_test.go @@ -0,0 +1,401 @@ +// 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 html + +import ( + "bytes" + "math" + "strings" + "testing" +) + +func TestNextJsCtx(t *testing.T) { + tests := []struct { + jsCtx jsCtx + s string + }{ + // Statement terminators precede regexps. + {jsCtxRegexp, ";"}, + // This is not airtight. + // ({ valueOf: function () { return 1 } } / 2) + // is valid JavaScript but in practice, devs do not do this. + // A block followed by a statement starting with a RegExp is + // much more common: + // while (x) {...} /foo/.test(x) || panic() + {jsCtxRegexp, "}"}, + // But member, call, grouping, and array expression terminators + // precede div ops. + {jsCtxDivOp, ")"}, + {jsCtxDivOp, "]"}, + // At the start of a primary expression, array, or expression + // statement, expect a regexp. + {jsCtxRegexp, "("}, + {jsCtxRegexp, "["}, + {jsCtxRegexp, "{"}, + // Assignment operators precede regexps as do all exclusively + // prefix and binary operators. + {jsCtxRegexp, "="}, + {jsCtxRegexp, "+="}, + {jsCtxRegexp, "*="}, + {jsCtxRegexp, "*"}, + {jsCtxRegexp, "!"}, + // Whether the + or - is infix or prefix, it cannot precede a + // div op. + {jsCtxRegexp, "+"}, + {jsCtxRegexp, "-"}, + // An incr/decr op precedes a div operator. + // This is not airtight. In (g = ++/h/i) a regexp follows a + // pre-increment operator, but in practice devs do not try to + // increment or decrement regular expressions. + // (g++/h/i) where ++ is a postfix operator on g is much more + // common. + {jsCtxDivOp, "--"}, + {jsCtxDivOp, "++"}, + {jsCtxDivOp, "x--"}, + // When we have many dashes or pluses, then they are grouped + // left to right. + {jsCtxRegexp, "x---"}, // A postfix -- then a -. + // return followed by a slash returns the regexp literal or the + // slash starts a regexp literal in an expression statement that + // is dead code. + {jsCtxRegexp, "return"}, + {jsCtxRegexp, "return "}, + {jsCtxRegexp, "return\t"}, + {jsCtxRegexp, "return\n"}, + {jsCtxRegexp, "return\u2028"}, + // Identifiers can be divided and cannot validly be preceded by + // a regular expressions. Semicolon insertion cannot happen + // between an identifier and a regular expression on a new line + // because the one token lookahead for semicolon insertion has + // to conclude that it could be a div binary op and treat it as + // such. + {jsCtxDivOp, "x"}, + {jsCtxDivOp, "x "}, + {jsCtxDivOp, "x\t"}, + {jsCtxDivOp, "x\n"}, + {jsCtxDivOp, "x\u2028"}, + {jsCtxDivOp, "preturn"}, + // Numbers precede div ops. + {jsCtxDivOp, "0"}, + // Dots that are part of a number are div preceders. + {jsCtxDivOp, "0."}, + } + + for _, test := range tests { + if nextJSCtx([]byte(test.s), jsCtxRegexp) != test.jsCtx { + t.Errorf("want %s got %q", test.jsCtx, test.s) + } + if nextJSCtx([]byte(test.s), jsCtxDivOp) != test.jsCtx { + t.Errorf("want %s got %q", test.jsCtx, test.s) + } + } + + if nextJSCtx([]byte(" "), jsCtxRegexp) != jsCtxRegexp { + t.Error("Blank tokens") + } + + if nextJSCtx([]byte(" "), jsCtxDivOp) != jsCtxDivOp { + t.Error("Blank tokens") + } +} + +func TestJSValEscaper(t *testing.T) { + tests := []struct { + x interface{} + js string + }{ + {int(42), " 42 "}, + {uint(42), " 42 "}, + {int16(42), " 42 "}, + {uint16(42), " 42 "}, + {int32(-42), " -42 "}, + {uint32(42), " 42 "}, + {int16(-42), " -42 "}, + {uint16(42), " 42 "}, + {int64(-42), " -42 "}, + {uint64(42), " 42 "}, + {uint64(1) << 53, " 9007199254740992 "}, + // ulp(1 << 53) > 1 so this loses precision in JS + // but it is still a representable integer literal. + {uint64(1)<<53 + 1, " 9007199254740993 "}, + {float32(1.0), " 1 "}, + {float32(-1.0), " -1 "}, + {float32(0.5), " 0.5 "}, + {float32(-0.5), " -0.5 "}, + {float32(1.0) / float32(256), " 0.00390625 "}, + {float32(0), " 0 "}, + {math.Copysign(0, -1), " -0 "}, + {float64(1.0), " 1 "}, + {float64(-1.0), " -1 "}, + {float64(0.5), " 0.5 "}, + {float64(-0.5), " -0.5 "}, + {float64(0), " 0 "}, + {math.Copysign(0, -1), " -0 "}, + {"", `""`}, + {"foo", `"foo"`}, + // Newlines. + {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`}, + // "\v" == "v" on IE 6 so use "\x0b" instead. + {"\t\x0b", `"\u0009\u000b"`}, + {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`}, + {[]interface{}{}, "[]"}, + {[]interface{}{42, "foo", nil}, `[42,"foo",null]`}, + {[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`}, + {"<!--", `"\u003c!--"`}, + {"-->", `"--\u003e"`}, + {"<![CDATA[", `"\u003c![CDATA["`}, + {"]]>", `"]]\u003e"`}, + {"</script", `"\u003c/script"`}, + {"\U0001D11E", "\"\U0001D11E\""}, // or "\uD834\uDD1E" + } + + for _, test := range tests { + if js := jsValEscaper(test.x); js != test.js { + t.Errorf("%+v: want\n\t%q\ngot\n\t%q", test.x, test.js, js) + } + // Make sure that escaping corner cases are not broken + // by nesting. + a := []interface{}{test.x} + want := "[" + strings.TrimSpace(test.js) + "]" + if js := jsValEscaper(a); js != want { + t.Errorf("%+v: want\n\t%q\ngot\n\t%q", a, want, js) + } + } +} + +func TestJSStrEscaper(t *testing.T) { + tests := []struct { + x interface{} + esc string + }{ + {"", ``}, + {"foo", `foo`}, + {"\u0000", `\0`}, + {"\t", `\t`}, + {"\n", `\n`}, + {"\r", `\r`}, + {"\u2028", `\u2028`}, + {"\u2029", `\u2029`}, + {"\\", `\\`}, + {"\\n", `\\n`}, + {"foo\r\nbar", `foo\r\nbar`}, + // Preserve attribute boundaries. + {`"`, `\x22`}, + {`'`, `\x27`}, + // Allow embedding in HTML without further escaping. + {`&`, `\x26amp;`}, + // Prevent breaking out of text node and element boundaries. + {"</script>", `\x3c\/script\x3e`}, + {"<![CDATA[", `\x3c![CDATA[`}, + {"]]>", `]]\x3e`}, + // http://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span + // "The text in style, script, title, and textarea elements + // must not have an escaping text span start that is not + // followed by an escaping text span end." + // Furthermore, spoofing an escaping text span end could lead + // to different interpretation of a </script> sequence otherwise + // masked by the escaping text span, and spoofing a start could + // allow regular text content to be interpreted as script + // allowing script execution via a combination of a JS string + // injection followed by an HTML text injection. + {"<!--", `\x3c!--`}, + {"-->", `--\x3e`}, + // From http://code.google.com/p/doctype/wiki/ArticleUtf7 + {"+ADw-script+AD4-alert(1)+ADw-/script+AD4-", + `\x2bADw-script\x2bAD4-alert(1)\x2bADw-\/script\x2bAD4-`, + }, + // Invalid UTF-8 sequence + {"foo\xA0bar", "foo\xA0bar"}, + // Invalid unicode scalar value. + {"foo\xed\xa0\x80bar", "foo\xed\xa0\x80bar"}, + } + + for _, test := range tests { + esc := jsStrEscaper(test.x) + if esc != test.esc { + t.Errorf("%q: want %q got %q", test.x, test.esc, esc) + } + } +} + +func TestJSRegexpEscaper(t *testing.T) { + tests := []struct { + x interface{} + esc string + }{ + {"", `(?:)`}, + {"foo", `foo`}, + {"\u0000", `\0`}, + {"\t", `\t`}, + {"\n", `\n`}, + {"\r", `\r`}, + {"\u2028", `\u2028`}, + {"\u2029", `\u2029`}, + {"\\", `\\`}, + {"\\n", `\\n`}, + {"foo\r\nbar", `foo\r\nbar`}, + // Preserve attribute boundaries. + {`"`, `\x22`}, + {`'`, `\x27`}, + // Allow embedding in HTML without further escaping. + {`&`, `\x26amp;`}, + // Prevent breaking out of text node and element boundaries. + {"</script>", `\x3c\/script\x3e`}, + {"<![CDATA[", `\x3c!\[CDATA\[`}, + {"]]>", `\]\]\x3e`}, + // Escaping text spans. + {"<!--", `\x3c!\-\-`}, + {"-->", `\-\-\x3e`}, + {"*", `\*`}, + {"+", `\x2b`}, + {"?", `\?`}, + {"[](){}", `\[\]\(\)\{\}`}, + {"$foo|x.y", `\$foo\|x\.y`}, + {"x^y", `x\^y`}, + } + + for _, test := range tests { + esc := jsRegexpEscaper(test.x) + if esc != test.esc { + t.Errorf("%q: want %q got %q", test.x, test.esc, esc) + } + } +} + +func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) { + input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" + + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !"#$%&'()*+,-./` + + `0123456789:;<=>?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\]^_` + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\x7f" + + "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") + + tests := []struct { + name string + escaper func(...interface{}) string + escaped string + }{ + { + "jsStrEscaper", + jsStrEscaper, + "\\0\x01\x02\x03\x04\x05\x06\x07" + + "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" + + "\x10\x11\x12\x13\x14\x15\x16\x17" + + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !\x22#$%\x26\x27()*\x2b,-.\/` + + `0123456789:;\x3c=\x3e?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\\]^_` + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\x7f" + + "\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E", + }, + { + "jsRegexpEscaper", + jsRegexpEscaper, + "\\0\x01\x02\x03\x04\x05\x06\x07" + + "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" + + "\x10\x11\x12\x13\x14\x15\x16\x17" + + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !\x22#\$%\x26\x27\(\)\*\x2b,\-\.\/` + + `0123456789:;\x3c=\x3e\?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ\[\\\]\^_` + + "`abcdefghijklmno" + + `pqrstuvwxyz\{\|\}~` + "\u007f" + + "\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E", + }, + } + + for _, test := range tests { + if s := test.escaper(input); s != test.escaped { + t.Errorf("%s once: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s) + continue + } + + // Escape it rune by rune to make sure that any + // fast-path checking does not break escaping. + var buf bytes.Buffer + for _, c := range input { + buf.WriteString(test.escaper(string(c))) + } + + if s := buf.String(); s != test.escaped { + t.Errorf("%s rune-wise: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s) + continue + } + } +} + +func BenchmarkJSValEscaperWithNum(b *testing.B) { + for i := 0; i < b.N; i++ { + jsValEscaper(3.141592654) + } +} + +func BenchmarkJSValEscaperWithStr(b *testing.B) { + for i := 0; i < b.N; i++ { + jsValEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>") + } +} + +func BenchmarkJSValEscaperWithStrNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + jsValEscaper("The quick, brown fox jumps over the lazy dog") + } +} + +func BenchmarkJSValEscaperWithObj(b *testing.B) { + o := struct { + S string + N int + }{ + "The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>\u2028", + 42, + } + for i := 0; i < b.N; i++ { + jsValEscaper(o) + } +} + +func BenchmarkJSValEscaperWithObjNoSpecials(b *testing.B) { + o := struct { + S string + N int + }{ + "The quick, brown fox jumps over the lazy dog", + 42, + } + for i := 0; i < b.N; i++ { + jsValEscaper(o) + } +} + +func BenchmarkJSStrEscaperNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + jsStrEscaper("The quick, brown fox jumps over the lazy dog.") + } +} + +func BenchmarkJSStrEscaper(b *testing.B) { + for i := 0; i < b.N; i++ { + jsStrEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>") + } +} + +func BenchmarkJSRegexpEscaperNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + jsRegexpEscaper("The quick, brown fox jumps over the lazy dog") + } +} + +func BenchmarkJSRegexpEscaper(b *testing.B) { + for i := 0; i < b.N; i++ { + jsRegexpEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>") + } +} diff --git a/libgo/go/exp/template/html/transition.go b/libgo/go/exp/template/html/transition.go new file mode 100644 index 00000000000..49a14511745 --- /dev/null +++ b/libgo/go/exp/template/html/transition.go @@ -0,0 +1,553 @@ +// 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 html + +import ( + "bytes" + "strings" +) + +// transitionFunc is the array of context transition functions for text nodes. +// A transition function takes a context and template text input, and returns +// the updated context and the number of bytes consumed from the front of the +// input. +var transitionFunc = [...]func(context, []byte) (context, int){ + stateText: tText, + stateTag: tTag, + stateAttrName: tAttrName, + stateAfterName: tAfterName, + stateBeforeValue: tBeforeValue, + stateHTMLCmt: tHTMLCmt, + stateRCDATA: tSpecialTagEnd, + stateAttr: tAttr, + stateURL: tURL, + stateJS: tJS, + stateJSDqStr: tJSDelimited, + stateJSSqStr: tJSDelimited, + stateJSRegexp: tJSDelimited, + stateJSBlockCmt: tBlockCmt, + stateJSLineCmt: tLineCmt, + stateCSS: tCSS, + stateCSSDqStr: tCSSStr, + stateCSSSqStr: tCSSStr, + stateCSSDqURL: tCSSStr, + stateCSSSqURL: tCSSStr, + stateCSSURL: tCSSStr, + stateCSSBlockCmt: tBlockCmt, + stateCSSLineCmt: tLineCmt, + stateError: tError, +} + +var commentStart = []byte("<!--") +var commentEnd = []byte("-->") + +// tText is the context transition function for the text state. +func tText(c context, s []byte) (context, int) { + k := 0 + for { + i := k + bytes.IndexByte(s[k:], '<') + if i < k || i+1 == len(s) { + return c, len(s) + } else if i+4 <= len(s) && bytes.Equal(commentStart, s[i:i+4]) { + return context{state: stateHTMLCmt}, i + 4 + } + i++ + end := false + if s[i] == '/' { + if i+1 == len(s) { + return c, len(s) + } + end, i = true, i+1 + } + j, e := eatTagName(s, i) + if j != i { + if end { + e = elementNone + } + // We've found an HTML tag. + return context{state: stateTag, element: e}, j + } + k = j + } + panic("unreachable") +} + +var elementContentType = [...]state{ + elementNone: stateText, + elementScript: stateJS, + elementStyle: stateCSS, + elementTextarea: stateRCDATA, + elementTitle: stateRCDATA, +} + +// tTag is the context transition function for the tag state. +func tTag(c context, s []byte) (context, int) { + // Find the attribute name. + i := eatWhiteSpace(s, 0) + if i == len(s) { + return c, len(s) + } + if s[i] == '>' { + return context{ + state: elementContentType[c.element], + element: c.element, + }, i + 1 + } + j, err := eatAttrName(s, i) + if err != nil { + return context{state: stateError, err: err}, len(s) + } + state, attr := stateTag, attrNone + if i == j { + return context{ + state: stateError, + err: errorf(ErrBadHTML, 0, "expected space, attr name, or end of tag, but got %q", s[i:]), + }, len(s) + } + switch attrType(string(s[i:j])) { + case contentTypeURL: + attr = attrURL + case contentTypeCSS: + attr = attrStyle + case contentTypeJS: + attr = attrScript + } + if j == len(s) { + state = stateAttrName + } else { + state = stateAfterName + } + return context{state: state, element: c.element, attr: attr}, j +} + +// tAttrName is the context transition function for stateAttrName. +func tAttrName(c context, s []byte) (context, int) { + i, err := eatAttrName(s, 0) + if err != nil { + return context{state: stateError, err: err}, len(s) + } else if i != len(s) { + c.state = stateAfterName + } + return c, i +} + +// tAfterName is the context transition function for stateAfterName. +func tAfterName(c context, s []byte) (context, int) { + // Look for the start of the value. + i := eatWhiteSpace(s, 0) + if i == len(s) { + return c, len(s) + } else if s[i] != '=' { + // Occurs due to tag ending '>', and valueless attribute. + c.state = stateTag + return c, i + } + c.state = stateBeforeValue + // Consume the "=". + return c, i + 1 +} + +var attrStartStates = [...]state{ + attrNone: stateAttr, + attrScript: stateJS, + attrStyle: stateCSS, + attrURL: stateURL, +} + +// tBeforeValue is the context transition function for stateBeforeValue. +func tBeforeValue(c context, s []byte) (context, int) { + i := eatWhiteSpace(s, 0) + if i == len(s) { + return c, len(s) + } + // Find the attribute delimiter. + delim := delimSpaceOrTagEnd + switch s[i] { + case '\'': + delim, i = delimSingleQuote, i+1 + case '"': + delim, i = delimDoubleQuote, i+1 + } + c.state, c.delim, c.attr = attrStartStates[c.attr], delim, attrNone + return c, i +} + +// tHTMLCmt is the context transition function for stateHTMLCmt. +func tHTMLCmt(c context, s []byte) (context, int) { + if i := bytes.Index(s, commentEnd); i != -1 { + return context{}, i + 3 + } + return c, len(s) +} + +// specialTagEndMarkers maps element types to the character sequence that +// case-insensitively signals the end of the special tag body. +var specialTagEndMarkers = [...]string{ + elementScript: "</script", + elementStyle: "</style", + elementTextarea: "</textarea", + elementTitle: "</title", +} + +// tSpecialTagEnd is the context transition function for raw text and RCDATA +// element states. +func tSpecialTagEnd(c context, s []byte) (context, int) { + if c.element != elementNone { + if i := strings.Index(strings.ToLower(string(s)), specialTagEndMarkers[c.element]); i != -1 { + return context{}, i + } + } + return c, len(s) +} + +// tAttr is the context transition function for the attribute state. +func tAttr(c context, s []byte) (context, int) { + return c, len(s) +} + +// tURL is the context transition function for the URL state. +func tURL(c context, s []byte) (context, int) { + if bytes.IndexAny(s, "#?") >= 0 { + c.urlPart = urlPartQueryOrFrag + } else if len(s) != eatWhiteSpace(s, 0) && c.urlPart == urlPartNone { + // HTML5 uses "Valid URL potentially surrounded by spaces" for + // attrs: http://www.w3.org/TR/html5/index.html#attributes-1 + c.urlPart = urlPartPreQuery + } + return c, len(s) +} + +// tJS is the context transition function for the JS state. +func tJS(c context, s []byte) (context, int) { + i := bytes.IndexAny(s, `"'/`) + if i == -1 { + // Entire input is non string, comment, regexp tokens. + c.jsCtx = nextJSCtx(s, c.jsCtx) + return c, len(s) + } + c.jsCtx = nextJSCtx(s[:i], c.jsCtx) + switch s[i] { + case '"': + c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp + case '\'': + c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp + case '/': + switch { + case i+1 < len(s) && s[i+1] == '/': + c.state, i = stateJSLineCmt, i+1 + case i+1 < len(s) && s[i+1] == '*': + c.state, i = stateJSBlockCmt, i+1 + case c.jsCtx == jsCtxRegexp: + c.state = stateJSRegexp + case c.jsCtx == jsCtxDivOp: + c.jsCtx = jsCtxRegexp + default: + return context{ + state: stateError, + err: errorf(ErrSlashAmbig, 0, "'/' could start a division or regexp: %.32q", s[i:]), + }, len(s) + } + default: + panic("unreachable") + } + return c, i + 1 +} + +// tJSDelimited is the context transition function for the JS string and regexp +// states. +func tJSDelimited(c context, s []byte) (context, int) { + specials := `\"` + switch c.state { + case stateJSSqStr: + specials = `\'` + case stateJSRegexp: + specials = `\/[]` + } + + k, inCharset := 0, false + for { + i := k + bytes.IndexAny(s[k:], specials) + if i < k { + break + } + switch s[i] { + case '\\': + i++ + if i == len(s) { + return context{ + state: stateError, + err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in JS string: %q", s), + }, len(s) + } + case '[': + inCharset = true + case ']': + inCharset = false + default: + // end delimiter + if !inCharset { + c.state, c.jsCtx = stateJS, jsCtxDivOp + return c, i + 1 + } + } + k = i + 1 + } + + if inCharset { + // This can be fixed by making context richer if interpolation + // into charsets is desired. + return context{ + state: stateError, + err: errorf(ErrPartialCharset, 0, "unfinished JS regexp charset: %q", s), + }, len(s) + } + + return c, len(s) +} + +var blockCommentEnd = []byte("*/") + +// tBlockCmt is the context transition function for /*comment*/ states. +func tBlockCmt(c context, s []byte) (context, int) { + i := bytes.Index(s, blockCommentEnd) + if i == -1 { + return c, len(s) + } + switch c.state { + case stateJSBlockCmt: + c.state = stateJS + case stateCSSBlockCmt: + c.state = stateCSS + default: + panic(c.state.String()) + } + return c, i + 2 +} + +// tLineCmt is the context transition function for //comment states. +func tLineCmt(c context, s []byte) (context, int) { + var lineTerminators string + var endState state + switch c.state { + case stateJSLineCmt: + lineTerminators, endState = "\n\r\u2028\u2029", stateJS + case stateCSSLineCmt: + lineTerminators, endState = "\n\f\r", stateCSS + // Line comments are not part of any published CSS standard but + // are supported by the 4 major browsers. + // This defines line comments as + // LINECOMMENT ::= "//" [^\n\f\d]* + // since http://www.w3.org/TR/css3-syntax/#SUBTOK-nl defines + // newlines: + // nl ::= #xA | #xD #xA | #xD | #xC + default: + panic(c.state.String()) + } + + i := bytes.IndexAny(s, lineTerminators) + if i == -1 { + return c, len(s) + } + c.state = endState + // Per section 7.4 of EcmaScript 5 : http://es5.github.com/#x7.4 + // "However, the LineTerminator at the end of the line is not + // considered to be part of the single-line comment; it is + // recognized separately by the lexical grammar and becomes part + // of the stream of input elements for the syntactic grammar." + return c, i +} + +// tCSS is the context transition function for the CSS state. +func tCSS(c context, s []byte) (context, int) { + // CSS quoted strings are almost never used except for: + // (1) URLs as in background: "/foo.png" + // (2) Multiword font-names as in font-family: "Times New Roman" + // (3) List separators in content values as in inline-lists: + // <style> + // ul.inlineList { list-style: none; padding:0 } + // ul.inlineList > li { display: inline } + // ul.inlineList > li:before { content: ", " } + // ul.inlineList > li:first-child:before { content: "" } + // </style> + // <ul class=inlineList><li>One<li>Two<li>Three</ul> + // (4) Attribute value selectors as in a[href="http://example.com/"] + // + // We conservatively treat all strings as URLs, but make some + // allowances to avoid confusion. + // + // In (1), our conservative assumption is justified. + // In (2), valid font names do not contain ':', '?', or '#', so our + // conservative assumption is fine since we will never transition past + // urlPartPreQuery. + // In (3), our protocol heuristic should not be tripped, and there + // should not be non-space content after a '?' or '#', so as long as + // we only %-encode RFC 3986 reserved characters we are ok. + // In (4), we should URL escape for URL attributes, and for others we + // have the attribute name available if our conservative assumption + // proves problematic for real code. + + k := 0 + for { + i := k + bytes.IndexAny(s[k:], `("'/`) + if i < k { + return c, len(s) + } + switch s[i] { + case '(': + // Look for url to the left. + p := bytes.TrimRight(s[:i], "\t\n\f\r ") + if endsWithCSSKeyword(p, "url") { + j := len(s) - len(bytes.TrimLeft(s[i+1:], "\t\n\f\r ")) + switch { + case j != len(s) && s[j] == '"': + c.state, j = stateCSSDqURL, j+1 + case j != len(s) && s[j] == '\'': + c.state, j = stateCSSSqURL, j+1 + default: + c.state = stateCSSURL + } + return c, j + } + case '/': + if i+1 < len(s) { + switch s[i+1] { + case '/': + c.state = stateCSSLineCmt + return c, i + 2 + case '*': + c.state = stateCSSBlockCmt + return c, i + 2 + } + } + case '"': + c.state = stateCSSDqStr + return c, i + 1 + case '\'': + c.state = stateCSSSqStr + return c, i + 1 + } + k = i + 1 + } + panic("unreachable") +} + +// tCSSStr is the context transition function for the CSS string and URL states. +func tCSSStr(c context, s []byte) (context, int) { + var endAndEsc string + switch c.state { + case stateCSSDqStr, stateCSSDqURL: + endAndEsc = `\"` + case stateCSSSqStr, stateCSSSqURL: + endAndEsc = `\'` + case stateCSSURL: + // Unquoted URLs end with a newline or close parenthesis. + // The below includes the wc (whitespace character) and nl. + endAndEsc = "\\\t\n\f\r )" + default: + panic(c.state.String()) + } + + k := 0 + for { + i := k + bytes.IndexAny(s[k:], endAndEsc) + if i < k { + c, nread := tURL(c, decodeCSS(s[k:])) + return c, k + nread + } + if s[i] == '\\' { + i++ + if i == len(s) { + return context{ + state: stateError, + err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in CSS string: %q", s), + }, len(s) + } + } else { + c.state = stateCSS + return c, i + 1 + } + c, _ = tURL(c, decodeCSS(s[:i+1])) + k = i + 1 + } + panic("unreachable") +} + +// tError is the context transition function for the error state. +func tError(c context, s []byte) (context, int) { + return c, len(s) +} + +// eatAttrName returns the largest j such that s[i:j] is an attribute name. +// It returns an error if s[i:] does not look like it begins with an +// attribute name, such as encountering a quote mark without a preceding +// equals sign. +func eatAttrName(s []byte, i int) (int, *Error) { + for j := i; j < len(s); j++ { + switch s[j] { + case ' ', '\t', '\n', '\f', '\r', '=', '>': + return j, nil + case '\'', '"', '<': + // These result in a parse warning in HTML5 and are + // indicative of serious problems if seen in an attr + // name in a template. + return -1, errorf(ErrBadHTML, 0, "%q in attribute name: %.32q", s[j:j+1], s) + default: + // No-op. + } + } + return len(s), nil +} + +var elementNameMap = map[string]element{ + "script": elementScript, + "style": elementStyle, + "textarea": elementTextarea, + "title": elementTitle, +} + +// asciiAlpha returns whether c is an ASCII letter. +func asciiAlpha(c byte) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' +} + +// asciiAlphaNum returns whether c is an ASCII letter or digit. +func asciiAlphaNum(c byte) bool { + return asciiAlpha(c) || '0' <= c && c <= '9' +} + +// eatTagName returns the largest j such that s[i:j] is a tag name and the tag type. +func eatTagName(s []byte, i int) (int, element) { + if i == len(s) || !asciiAlpha(s[i]) { + return i, elementNone + } + j := i + 1 + for j < len(s) { + x := s[j] + if asciiAlphaNum(x) { + j++ + continue + } + // Allow "x-y" or "x:y" but not "x-", "-y", or "x--y". + if (x == ':' || x == '-') && j+1 < len(s) && asciiAlphaNum(s[j+1]) { + j += 2 + continue + } + break + } + return j, elementNameMap[strings.ToLower(string(s[i:j]))] +} + +// eatWhiteSpace returns the largest j such that s[i:j] is white space. +func eatWhiteSpace(s []byte, i int) int { + for j := i; j < len(s); j++ { + switch s[j] { + case ' ', '\t', '\n', '\f', '\r': + // No-op. + default: + return j + } + } + return len(s) +} diff --git a/libgo/go/exp/template/html/url.go b/libgo/go/exp/template/html/url.go new file mode 100644 index 00000000000..5b19df08404 --- /dev/null +++ b/libgo/go/exp/template/html/url.go @@ -0,0 +1,105 @@ +// 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 html + +import ( + "bytes" + "fmt" + "strings" +) + +// urlFilter returns its input unless it contains an unsafe protocol in which +// case it defangs the entire URL. +func urlFilter(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeURL { + return s + } + if i := strings.IndexRune(s, ':'); i >= 0 && strings.IndexRune(s[:i], '/') < 0 { + protocol := strings.ToLower(s[:i]) + if protocol != "http" && protocol != "https" && protocol != "mailto" { + return "#" + filterFailsafe + } + } + return s +} + +// urlEscaper produces an output that can be embedded in a URL query. +// The output can be embedded in an HTML attribute without further escaping. +func urlEscaper(args ...interface{}) string { + return urlProcessor(false, args...) +} + +// urlEscaper normalizes URL content so it can be embedded in a quote-delimited +// string or parenthesis delimited url(...). +// The normalizer does not encode all HTML specials. Specifically, it does not +// encode '&' so correct embedding in an HTML attribute requires escaping of +// '&' to '&'. +func urlNormalizer(args ...interface{}) string { + return urlProcessor(true, args...) +} + +// urlProcessor normalizes (when norm is true) or escapes its input to produce +// a valid hierarchical or opaque URL part. +func urlProcessor(norm bool, args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeURL { + norm = true + } + var b bytes.Buffer + written := 0 + // The byte loop below assumes that all URLs use UTF-8 as the + // content-encoding. This is similar to the URI to IRI encoding scheme + // defined in section 3.1 of RFC 3987, and behaves the same as the + // EcmaScript builtin encodeURIComponent. + // It should not cause any misencoding of URLs in pages with + // Content-type: text/html;charset=UTF-8. + for i, n := 0, len(s); i < n; i++ { + c := s[i] + switch c { + // Single quote and parens are sub-delims in RFC 3986, but we + // escape them so the output can be embedded in in single + // quoted attributes and unquoted CSS url(...) constructs. + // Single quotes are reserved in URLs, but are only used in + // the obsolete "mark" rule in an appendix in RFC 3986 + // so can be safely encoded. + case '!', '#', '$', '&', '*', '+', ',', '/', ':', ';', '=', '?', '@', '[', ']': + if norm { + continue + } + // Unreserved according to RFC 3986 sec 2.3 + // "For consistency, percent-encoded octets in the ranges of + // ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), + // period (%2E), underscore (%5F), or tilde (%7E) should not be + // created by URI producers + case '-', '.', '_', '~': + continue + case '%': + // When normalizing do not re-encode valid escapes. + if norm && i+2 < len(s) && isHex(s[i+1]) && isHex(s[i+2]) { + continue + } + default: + // Unreserved according to RFC 3986 sec 2.3 + if 'a' <= c && c <= 'z' { + continue + } + if 'A' <= c && c <= 'Z' { + continue + } + if '0' <= c && c <= '9' { + continue + } + } + b.WriteString(s[written:i]) + fmt.Fprintf(&b, "%%%02x", c) + written = i + 1 + } + if written == 0 { + return s + } + b.WriteString(s[written:]) + return b.String() +} diff --git a/libgo/go/exp/template/html/url_test.go b/libgo/go/exp/template/html/url_test.go new file mode 100644 index 00000000000..b84623151c7 --- /dev/null +++ b/libgo/go/exp/template/html/url_test.go @@ -0,0 +1,112 @@ +// 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 html + +import ( + "testing" +) + +func TestURLNormalizer(t *testing.T) { + tests := []struct { + url, want string + }{ + {"", ""}, + { + "http://example.com:80/foo/bar?q=foo%20&bar=x+y#frag", + "http://example.com:80/foo/bar?q=foo%20&bar=x+y#frag", + }, + {" ", "%20"}, + {"%7c", "%7c"}, + {"%7C", "%7C"}, + {"%2", "%252"}, + {"%", "%25"}, + {"%z", "%25z"}, + {"/foo|bar/%5c\u1234", "/foo%7cbar/%5c%e1%88%b4"}, + } + for _, test := range tests { + if got := urlNormalizer(test.url); test.want != got { + t.Errorf("%q: want\n\t%q\nbut got\n\t%q", test.url, test.want, got) + } + if test.want != urlNormalizer(test.want) { + t.Errorf("not idempotent: %q", test.want) + } + } +} + +func TestURLFilters(t *testing.T) { + input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" + + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !"#$%&'()*+,-./` + + `0123456789:;<=>?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\]^_` + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\x7f" + + "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") + + tests := []struct { + name string + escaper func(...interface{}) string + escaped string + }{ + { + "urlEscaper", + urlEscaper, + "%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f" + + "%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f" + + "%20%21%22%23%24%25%26%27%28%29%2a%2b%2c-.%2f" + + "0123456789%3a%3b%3c%3d%3e%3f" + + "%40ABCDEFGHIJKLMNO" + + "PQRSTUVWXYZ%5b%5c%5d%5e_" + + "%60abcdefghijklmno" + + "pqrstuvwxyz%7b%7c%7d~%7f" + + "%c2%a0%c4%80%e2%80%a8%e2%80%a9%ef%bb%bf%f0%9d%84%9e", + }, + { + "urlNormalizer", + urlNormalizer, + "%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f" + + "%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f" + + "%20!%22#$%25&%27%28%29*+,-./" + + "0123456789:;%3c=%3e?" + + "@ABCDEFGHIJKLMNO" + + "PQRSTUVWXYZ[%5c]%5e_" + + "%60abcdefghijklmno" + + "pqrstuvwxyz%7b%7c%7d~%7f" + + "%c2%a0%c4%80%e2%80%a8%e2%80%a9%ef%bb%bf%f0%9d%84%9e", + }, + } + + for _, test := range tests { + if s := test.escaper(input); s != test.escaped { + t.Errorf("%s: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s) + continue + } + } +} + +func BenchmarkURLEscaper(b *testing.B) { + for i := 0; i < b.N; i++ { + urlEscaper("http://example.com:80/foo?q=bar%20&baz=x+y#frag") + } +} + +func BenchmarkURLEscaperNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + urlEscaper("TheQuickBrownFoxJumpsOverTheLazyDog.") + } +} + +func BenchmarkURLNormalizer(b *testing.B) { + for i := 0; i < b.N; i++ { + urlNormalizer("The quick brown fox jumps over the lazy dog.\n") + } +} + +func BenchmarkURLNormalizerNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + urlNormalizer("http://example.com:80/foo?q=bar%20&baz=x+y#frag") + } +} diff --git a/libgo/go/exp/terminal/shell.go b/libgo/go/exp/terminal/shell.go new file mode 100644 index 00000000000..e3f584774e3 --- /dev/null +++ b/libgo/go/exp/terminal/shell.go @@ -0,0 +1,359 @@ +// 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 terminal + +import ( + "os" + "io" +) + +// Shell contains the state for running a VT100 terminal that is capable of +// reading lines of input. +type Shell struct { + c io.ReadWriter + prompt string + + // line is the current line being entered. + line []byte + // pos is the logical position of the cursor in line + pos int + + // cursorX contains the current X value of the cursor where the left + // edge is 0. cursorY contains the row number where the first row of + // the current line is 0. + cursorX, cursorY int + // maxLine is the greatest value of cursorY so far. + maxLine int + + termWidth, termHeight int + + // outBuf contains the terminal data to be sent. + outBuf []byte + // remainder contains the remainder of any partial key sequences after + // a read. It aliases into inBuf. + remainder []byte + inBuf [256]byte +} + +// NewShell runs a VT100 terminal on the given ReadWriter. If the ReadWriter is +// a local terminal, that terminal must first have been put into raw mode. +// prompt is a string that is written at the start of each input line (i.e. +// "> "). +func NewShell(c io.ReadWriter, prompt string) *Shell { + return &Shell{ + c: c, + prompt: prompt, + termWidth: 80, + termHeight: 24, + } +} + +const ( + keyCtrlD = 4 + keyEnter = '\r' + keyEscape = 27 + keyBackspace = 127 + keyUnknown = 256 + iota + keyUp + keyDown + keyLeft + keyRight + keyAltLeft + keyAltRight +) + +// bytesToKey tries to parse a key sequence from b. If successful, it returns +// the key and the remainder of the input. Otherwise it returns -1. +func bytesToKey(b []byte) (int, []byte) { + if len(b) == 0 { + return -1, nil + } + + if b[0] != keyEscape { + return int(b[0]), b[1:] + } + + if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { + switch b[2] { + case 'A': + return keyUp, b[3:] + case 'B': + return keyDown, b[3:] + case 'C': + return keyRight, b[3:] + case 'D': + return keyLeft, b[3:] + } + } + + if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { + switch b[5] { + case 'C': + return keyAltRight, b[6:] + case 'D': + return keyAltLeft, b[6:] + } + } + + // If we get here then we have a key that we don't recognise, or a + // partial sequence. It's not clear how one should find the end of a + // sequence without knowing them all, but it seems that [a-zA-Z] only + // appears at the end of a sequence. + for i, c := range b[0:] { + if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' { + return keyUnknown, b[i+1:] + } + } + + return -1, b +} + +// queue appends data to the end of ss.outBuf +func (ss *Shell) queue(data []byte) { + if len(ss.outBuf)+len(data) > cap(ss.outBuf) { + newOutBuf := make([]byte, len(ss.outBuf), 2*(len(ss.outBuf)+len(data))) + copy(newOutBuf, ss.outBuf) + ss.outBuf = newOutBuf + } + + oldLen := len(ss.outBuf) + ss.outBuf = ss.outBuf[:len(ss.outBuf)+len(data)] + copy(ss.outBuf[oldLen:], data) +} + +var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'} + +func isPrintable(key int) bool { + return key >= 32 && key < 127 +} + +// moveCursorToPos appends data to ss.outBuf which will move the cursor to the +// given, logical position in the text. +func (ss *Shell) moveCursorToPos(pos int) { + x := len(ss.prompt) + pos + y := x / ss.termWidth + x = x % ss.termWidth + + up := 0 + if y < ss.cursorY { + up = ss.cursorY - y + } + + down := 0 + if y > ss.cursorY { + down = y - ss.cursorY + } + + left := 0 + if x < ss.cursorX { + left = ss.cursorX - x + } + + right := 0 + if x > ss.cursorX { + right = x - ss.cursorX + } + + movement := make([]byte, 3*(up+down+left+right)) + m := movement + for i := 0; i < up; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'A' + m = m[3:] + } + for i := 0; i < down; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'B' + m = m[3:] + } + for i := 0; i < left; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'D' + m = m[3:] + } + for i := 0; i < right; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'C' + m = m[3:] + } + + ss.cursorX = x + ss.cursorY = y + ss.queue(movement) +} + +const maxLineLength = 4096 + +// handleKey processes the given key and, optionally, returns a line of text +// that the user has entered. +func (ss *Shell) handleKey(key int) (line string, ok bool) { + switch key { + case keyBackspace: + if ss.pos == 0 { + return + } + ss.pos-- + + copy(ss.line[ss.pos:], ss.line[1+ss.pos:]) + ss.line = ss.line[:len(ss.line)-1] + ss.writeLine(ss.line[ss.pos:]) + ss.moveCursorToPos(ss.pos) + ss.queue(eraseUnderCursor) + case keyAltLeft: + // move left by a word. + if ss.pos == 0 { + return + } + ss.pos-- + for ss.pos > 0 { + if ss.line[ss.pos] != ' ' { + break + } + ss.pos-- + } + for ss.pos > 0 { + if ss.line[ss.pos] == ' ' { + ss.pos++ + break + } + ss.pos-- + } + ss.moveCursorToPos(ss.pos) + case keyAltRight: + // move right by a word. + for ss.pos < len(ss.line) { + if ss.line[ss.pos] == ' ' { + break + } + ss.pos++ + } + for ss.pos < len(ss.line) { + if ss.line[ss.pos] != ' ' { + break + } + ss.pos++ + } + ss.moveCursorToPos(ss.pos) + case keyLeft: + if ss.pos == 0 { + return + } + ss.pos-- + ss.moveCursorToPos(ss.pos) + case keyRight: + if ss.pos == len(ss.line) { + return + } + ss.pos++ + ss.moveCursorToPos(ss.pos) + case keyEnter: + ss.moveCursorToPos(len(ss.line)) + ss.queue([]byte("\r\n")) + line = string(ss.line) + ok = true + ss.line = ss.line[:0] + ss.pos = 0 + ss.cursorX = 0 + ss.cursorY = 0 + ss.maxLine = 0 + default: + if !isPrintable(key) { + return + } + if len(ss.line) == maxLineLength { + return + } + if len(ss.line) == cap(ss.line) { + newLine := make([]byte, len(ss.line), 2*(1+len(ss.line))) + copy(newLine, ss.line) + ss.line = newLine + } + ss.line = ss.line[:len(ss.line)+1] + copy(ss.line[ss.pos+1:], ss.line[ss.pos:]) + ss.line[ss.pos] = byte(key) + ss.writeLine(ss.line[ss.pos:]) + ss.pos++ + ss.moveCursorToPos(ss.pos) + } + return +} + +func (ss *Shell) writeLine(line []byte) { + for len(line) != 0 { + if ss.cursorX == ss.termWidth { + ss.queue([]byte("\r\n")) + ss.cursorX = 0 + ss.cursorY++ + if ss.cursorY > ss.maxLine { + ss.maxLine = ss.cursorY + } + } + + remainingOnLine := ss.termWidth - ss.cursorX + todo := len(line) + if todo > remainingOnLine { + todo = remainingOnLine + } + ss.queue(line[:todo]) + ss.cursorX += todo + line = line[todo:] + } +} + +func (ss *Shell) Write(buf []byte) (n int, err os.Error) { + return ss.c.Write(buf) +} + +// ReadLine returns a line of input from the terminal. +func (ss *Shell) ReadLine() (line string, err os.Error) { + ss.writeLine([]byte(ss.prompt)) + ss.c.Write(ss.outBuf) + ss.outBuf = ss.outBuf[:0] + + for { + // ss.remainder is a slice at the beginning of ss.inBuf + // containing a partial key sequence + readBuf := ss.inBuf[len(ss.remainder):] + var n int + n, err = ss.c.Read(readBuf) + if err != nil { + return + } + + if err == nil { + ss.remainder = ss.inBuf[:n+len(ss.remainder)] + rest := ss.remainder + lineOk := false + for !lineOk { + var key int + key, rest = bytesToKey(rest) + if key < 0 { + break + } + if key == keyCtrlD { + return "", os.EOF + } + line, lineOk = ss.handleKey(key) + } + if len(rest) > 0 { + n := copy(ss.inBuf[:], rest) + ss.remainder = ss.inBuf[:n] + } else { + ss.remainder = nil + } + ss.c.Write(ss.outBuf) + ss.outBuf = ss.outBuf[:0] + if lineOk { + return + } + continue + } + } + panic("unreachable") +} diff --git a/libgo/go/exp/terminal/shell_test.go b/libgo/go/exp/terminal/shell_test.go new file mode 100644 index 00000000000..2bbe4a4f8f9 --- /dev/null +++ b/libgo/go/exp/terminal/shell_test.go @@ -0,0 +1,110 @@ +// 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 terminal + +import ( + "testing" + "os" +) + +type MockTerminal struct { + toSend []byte + bytesPerRead int + received []byte +} + +func (c *MockTerminal) Read(data []byte) (n int, err os.Error) { + n = len(data) + if n == 0 { + return + } + if n > len(c.toSend) { + n = len(c.toSend) + } + if n == 0 { + return 0, os.EOF + } + if c.bytesPerRead > 0 && n > c.bytesPerRead { + n = c.bytesPerRead + } + copy(data, c.toSend[:n]) + c.toSend = c.toSend[n:] + return +} + +func (c *MockTerminal) Write(data []byte) (n int, err os.Error) { + c.received = append(c.received, data...) + return len(data), nil +} + +func TestClose(t *testing.T) { + c := &MockTerminal{} + ss := NewShell(c, "> ") + line, err := ss.ReadLine() + if line != "" { + t.Errorf("Expected empty line but got: %s", line) + } + if err != os.EOF { + t.Errorf("Error should have been EOF but got: %s", err) + } +} + +var keyPressTests = []struct { + in string + line string + err os.Error +}{ + { + "", + "", + os.EOF, + }, + { + "\r", + "", + nil, + }, + { + "foo\r", + "foo", + nil, + }, + { + "a\x1b[Cb\r", // right + "ab", + nil, + }, + { + "a\x1b[Db\r", // left + "ba", + nil, + }, + { + "a\177b\r", // backspace + "b", + nil, + }, +} + +func TestKeyPresses(t *testing.T) { + for i, test := range keyPressTests { + for j := 0; j < len(test.in); j++ { + c := &MockTerminal{ + toSend: []byte(test.in), + bytesPerRead: j, + } + ss := NewShell(c, "> ") + line, err := ss.ReadLine() + if line != test.line { + t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line) + break + } + if err != test.err { + t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err) + break + } + } + } +} diff --git a/libgo/go/exp/terminal/terminal.go b/libgo/go/exp/terminal/terminal.go new file mode 100644 index 00000000000..aacd90905f5 --- /dev/null +++ b/libgo/go/exp/terminal/terminal.go @@ -0,0 +1,103 @@ +// 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 terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err.String()) +// } +// defer terminal.Restore(0, oldState) +package terminal + +import ( + "os" + "syscall" + "unsafe" +) + +// State contains the state of a terminal. +type State struct { + termios syscall.Termios +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + var termios syscall.Termios + _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return e == 0 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, os.Error) { + var oldState State + if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); e != 0 { + return nil, os.Errno(e) + } + + newState := oldState.termios + newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF + newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG + if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); e != 0 { + return nil, os.Errno(e) + } + + return &oldState, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) os.Error { + _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) + return os.Errno(e) +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, os.Error) { + var oldState syscall.Termios + if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { + return nil, os.Errno(e) + } + + newState := oldState + newState.Lflag &^= syscall.ECHO + if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); e != 0 { + return nil, os.Errno(e) + } + + defer func() { + syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) + }() + + var buf [16]byte + var ret []byte + for { + n, errno := syscall.Read(fd, buf[:]) + if errno != 0 { + return nil, os.Errno(errno) + } + if n == 0 { + if len(ret) == 0 { + return nil, os.EOF + } + break + } + if buf[n-1] == '\n' { + n-- + } + ret = append(ret, buf[:n]...) + if n < len(buf) { + break + } + } + + return ret, nil +} diff --git a/libgo/go/exp/types/check.go b/libgo/go/exp/types/check.go new file mode 100644 index 00000000000..87e3e93da73 --- /dev/null +++ b/libgo/go/exp/types/check.go @@ -0,0 +1,226 @@ +// 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. + +// This file implements the Check function, which typechecks a package. + +package types + +import ( + "fmt" + "go/ast" + "go/scanner" + "go/token" + "os" + "strconv" +) + +const debug = false + +type checker struct { + fset *token.FileSet + scanner.ErrorVector + types map[ast.Expr]Type +} + +func (c *checker) errorf(pos token.Pos, format string, args ...interface{}) string { + msg := fmt.Sprintf(format, args...) + c.Error(c.fset.Position(pos), msg) + return msg +} + +// collectFields collects struct fields tok = token.STRUCT), interface methods +// (tok = token.INTERFACE), and function arguments/results (tok = token.FUNC). +func (c *checker) collectFields(tok token.Token, list *ast.FieldList, cycleOk bool) (fields ObjList, tags []string, isVariadic bool) { + if list != nil { + for _, field := range list.List { + ftype := field.Type + if t, ok := ftype.(*ast.Ellipsis); ok { + ftype = t.Elt + isVariadic = true + } + typ := c.makeType(ftype, cycleOk) + tag := "" + if field.Tag != nil { + assert(field.Tag.Kind == token.STRING) + tag, _ = strconv.Unquote(field.Tag.Value) + } + if len(field.Names) > 0 { + // named fields + for _, name := range field.Names { + obj := name.Obj + obj.Type = typ + fields = append(fields, obj) + if tok == token.STRUCT { + tags = append(tags, tag) + } + } + } else { + // anonymous field + switch tok { + case token.STRUCT: + tags = append(tags, tag) + fallthrough + case token.FUNC: + obj := ast.NewObj(ast.Var, "") + obj.Type = typ + fields = append(fields, obj) + case token.INTERFACE: + utyp := Underlying(typ) + if typ, ok := utyp.(*Interface); ok { + // TODO(gri) This is not good enough. Check for double declarations! + fields = append(fields, typ.Methods...) + } else if _, ok := utyp.(*Bad); !ok { + // if utyp is Bad, don't complain (the root cause was reported before) + c.errorf(ftype.Pos(), "interface contains embedded non-interface type") + } + default: + panic("unreachable") + } + } + } + } + return +} + +// makeType makes a new type for an AST type specification x or returns +// the type referred to by a type name x. If cycleOk is set, a type may +// refer to itself directly or indirectly; otherwise cycles are errors. +// +func (c *checker) makeType(x ast.Expr, cycleOk bool) (typ Type) { + if debug { + fmt.Printf("makeType (cycleOk = %v)\n", cycleOk) + ast.Print(c.fset, x) + defer func() { + fmt.Printf("-> %T %v\n\n", typ, typ) + }() + } + + switch t := x.(type) { + case *ast.BadExpr: + return &Bad{} + + case *ast.Ident: + // type name + obj := t.Obj + if obj == nil { + // unresolved identifier (error has been reported before) + return &Bad{Msg: "unresolved identifier"} + } + if obj.Kind != ast.Typ { + msg := c.errorf(t.Pos(), "%s is not a type", t.Name) + return &Bad{Msg: msg} + } + c.checkObj(obj, cycleOk) + if !cycleOk && obj.Type.(*Name).Underlying == nil { + // TODO(gri) Enable this message again once its position + // is independent of the underlying map implementation. + // msg := c.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name) + msg := "illegal cycle" + return &Bad{Msg: msg} + } + return obj.Type.(Type) + + case *ast.ParenExpr: + return c.makeType(t.X, cycleOk) + + case *ast.SelectorExpr: + // qualified identifier + // TODO (gri) eventually, this code belongs to expression + // type checking - here for the time being + if ident, ok := t.X.(*ast.Ident); ok { + if obj := ident.Obj; obj != nil { + if obj.Kind != ast.Pkg { + msg := c.errorf(ident.Pos(), "%s is not a package", obj.Name) + return &Bad{Msg: msg} + } + // TODO(gri) we have a package name but don't + // have the mapping from package name to package + // scope anymore (created in ast.NewPackage). + return &Bad{} // for now + } + } + // TODO(gri) can this really happen (the parser should have excluded this)? + msg := c.errorf(t.Pos(), "expected qualified identifier") + return &Bad{Msg: msg} + + case *ast.StarExpr: + return &Pointer{Base: c.makeType(t.X, true)} + + case *ast.ArrayType: + if t.Len != nil { + // TODO(gri) compute length + return &Array{Elt: c.makeType(t.Elt, cycleOk)} + } + return &Slice{Elt: c.makeType(t.Elt, true)} + + case *ast.StructType: + fields, tags, _ := c.collectFields(token.STRUCT, t.Fields, cycleOk) + return &Struct{Fields: fields, Tags: tags} + + case *ast.FuncType: + params, _, _ := c.collectFields(token.FUNC, t.Params, true) + results, _, isVariadic := c.collectFields(token.FUNC, t.Results, true) + return &Func{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic} + + case *ast.InterfaceType: + methods, _, _ := c.collectFields(token.INTERFACE, t.Methods, cycleOk) + methods.Sort() + return &Interface{Methods: methods} + + case *ast.MapType: + return &Map{Key: c.makeType(t.Key, true), Elt: c.makeType(t.Key, true)} + + case *ast.ChanType: + return &Chan{Dir: t.Dir, Elt: c.makeType(t.Value, true)} + } + + panic(fmt.Sprintf("unreachable (%T)", x)) +} + +// checkObj type checks an object. +func (c *checker) checkObj(obj *ast.Object, ref bool) { + if obj.Type != nil { + // object has already been type checked + return + } + + switch obj.Kind { + case ast.Bad: + // ignore + + case ast.Con: + // TODO(gri) complete this + + case ast.Typ: + typ := &Name{Obj: obj} + obj.Type = typ // "mark" object so recursion terminates + typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref)) + + case ast.Var: + // TODO(gri) complete this + + case ast.Fun: + // TODO(gri) complete this + + default: + panic("unreachable") + } +} + +// Check typechecks a package. +// It augments the AST by assigning types to all ast.Objects and returns a map +// of types for all expression nodes in statements, and a scanner.ErrorList if +// there are errors. +// +func Check(fset *token.FileSet, pkg *ast.Package) (types map[ast.Expr]Type, err os.Error) { + var c checker + c.fset = fset + c.types = make(map[ast.Expr]Type) + + for _, obj := range pkg.Scope.Objects { + c.checkObj(obj, false) + } + + return c.types, c.GetError(scanner.NoMultiples) +} diff --git a/libgo/go/exp/types/check_test.go b/libgo/go/exp/types/check_test.go new file mode 100644 index 00000000000..034acd00de5 --- /dev/null +++ b/libgo/go/exp/types/check_test.go @@ -0,0 +1,215 @@ +// 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. + +// This file implements a typechecker test harness. The packages specified +// in tests are typechecked. Error messages reported by the typechecker are +// compared against the error messages expected in the test files. +// +// Expected errors are indicated in the test files by putting a comment +// of the form /* ERROR "rx" */ immediately following an offending token. +// The harness will verify that an error matching the regular expression +// rx is reported at that source position. Consecutive comments may be +// used to indicate multiple errors for the same token position. +// +// For instance, the following test file indicates that a "not declared" +// error should be reported for the undeclared variable x: +// +// package p +// func f() { +// _ = x /* ERROR "not declared" */ + 1 +// } + +package types + +import ( + "fmt" + "go/ast" + "go/parser" + "go/scanner" + "go/token" + "io/ioutil" + "os" + "regexp" + "testing" +) + +// The test filenames do not end in .go so that they are invisible +// to gofmt since they contain comments that must not change their +// positions relative to surrounding tokens. + +var tests = []struct { + name string + files []string +}{ + {"test0", []string{"testdata/test0.src"}}, +} + +var fset = token.NewFileSet() + +// TODO(gri) This functionality should be in token.Fileset. +func getFile(filename string) *token.File { + for f := range fset.Files() { + if f.Name() == filename { + return f + } + } + return nil +} + +// TODO(gri) This functionality should be in token.Fileset. +func getPos(filename string, offset int) token.Pos { + if f := getFile(filename); f != nil { + return f.Pos(offset) + } + return token.NoPos +} + +// TODO(gri) Need to revisit parser interface. We should be able to use parser.ParseFiles +// or a similar function instead. +func parseFiles(t *testing.T, testname string, filenames []string) (map[string]*ast.File, os.Error) { + files := make(map[string]*ast.File) + var errors scanner.ErrorList + for _, filename := range filenames { + if _, exists := files[filename]; exists { + t.Fatalf("%s: duplicate file %s", testname, filename) + } + file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors) + if file == nil { + t.Fatalf("%s: could not parse file %s", testname, filename) + } + files[filename] = file + if err != nil { + // if the parser returns a non-scanner.ErrorList error + // the file couldn't be read in the first place and + // file == nil; in that case we shouldn't reach here + errors = append(errors, err.(scanner.ErrorList)...) + } + + } + return files, errors +} + +// ERROR comments must be of the form /* ERROR "rx" */ and rx is +// a regular expression that matches the expected error message. +// +var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`) + +// expectedErrors collects the regular expressions of ERROR comments found +// in files and returns them as a map of error positions to error messages. +// +func expectedErrors(t *testing.T, testname string, files map[string]*ast.File) map[token.Pos]string { + errors := make(map[token.Pos]string) + for filename := range files { + src, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatalf("%s: could not read %s", testname, filename) + } + + var s scanner.Scanner + // file was parsed already - do not add it again to the file + // set otherwise the position information returned here will + // not match the position information collected by the parser + s.Init(getFile(filename), src, nil, scanner.ScanComments) + var prev token.Pos // position of last non-comment token + + scanFile: + for { + pos, tok, lit := s.Scan() + switch tok { + case token.EOF: + break scanFile + case token.COMMENT: + s := errRx.FindStringSubmatch(lit) + if len(s) == 2 { + errors[prev] = string(s[1]) + } + default: + prev = pos + } + } + } + return errors +} + +func eliminate(t *testing.T, expected map[token.Pos]string, errors os.Error) { + if errors == nil { + return + } + for _, error := range errors.(scanner.ErrorList) { + // error.Pos is a token.Position, but we want + // a token.Pos so we can do a map lookup + // TODO(gri) Need to move scanner.Errors over + // to use token.Pos and file set info. + pos := getPos(error.Pos.Filename, error.Pos.Offset) + if msg, found := expected[pos]; found { + // we expect a message at pos; check if it matches + rx, err := regexp.Compile(msg) + if err != nil { + t.Errorf("%s: %v", error.Pos, err) + continue + } + if match := rx.MatchString(error.Msg); !match { + t.Errorf("%s: %q does not match %q", error.Pos, error.Msg, msg) + continue + } + // we have a match - eliminate this error + delete(expected, pos) + } else { + // To keep in mind when analyzing failed test output: + // If the same error position occurs multiple times in errors, + // this message will be triggered (because the first error at + // the position removes this position from the expected errors). + t.Errorf("%s: no (multiple?) error expected, but found: %s", error.Pos, error.Msg) + } + } +} + +func check(t *testing.T, testname string, testfiles []string) { + // TODO(gri) Eventually all these different phases should be + // subsumed into a single function call that takes + // a set of files and creates a fully resolved and + // type-checked AST. + + files, err := parseFiles(t, testname, testfiles) + + // we are expecting the following errors + // (collect these after parsing the files so that + // they are found in the file set) + errors := expectedErrors(t, testname, files) + + // verify errors returned by the parser + eliminate(t, errors, err) + + // verify errors returned after resolving identifiers + pkg, err := ast.NewPackage(fset, files, GcImporter, Universe) + eliminate(t, errors, err) + + // verify errors returned by the typechecker + _, err = Check(fset, pkg) + eliminate(t, errors, err) + + // there should be no expected errors left + if len(errors) > 0 { + t.Errorf("%s: %d errors not reported:", testname, len(errors)) + for pos, msg := range errors { + t.Errorf("%s: %s\n", fset.Position(pos), msg) + } + } +} + +func TestCheck(t *testing.T) { + // For easy debugging w/o changing the testing code, + // if there is a local test file, only test that file. + const testfile = "test.go" + if fi, err := os.Stat(testfile); err == nil && fi.IsRegular() { + fmt.Printf("WARNING: Testing only %s (remove it to run all tests)\n", testfile) + check(t, testfile, []string{testfile}) + return + } + + // Otherwise, run all the tests. + for _, test := range tests { + check(t, test.name, test.files) + } +} diff --git a/libgo/go/exp/types/const.go b/libgo/go/exp/types/const.go new file mode 100644 index 00000000000..1ef95d9f952 --- /dev/null +++ b/libgo/go/exp/types/const.go @@ -0,0 +1,332 @@ +// 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. + +// This file implements operations on ideal constants. + +package types + +import ( + "big" + "go/token" + "strconv" +) + +// TODO(gri) Consider changing the API so Const is an interface +// and operations on consts don't have to type switch. + +// A Const implements an ideal constant Value. +// The zero value z for a Const is not a valid constant value. +type Const struct { + // representation of constant values: + // ideal bool -> bool + // ideal int -> *big.Int + // ideal float -> *big.Rat + // ideal complex -> cmplx + // ideal string -> string + val interface{} +} + +// Representation of complex values. +type cmplx struct { + re, im *big.Rat +} + +func assert(cond bool) { + if !cond { + panic("go/types internal error: assertion failed") + } +} + +// MakeConst makes an ideal constant from a literal +// token and the corresponding literal string. +func MakeConst(tok token.Token, lit string) Const { + switch tok { + case token.INT: + var x big.Int + _, ok := x.SetString(lit, 0) + assert(ok) + return Const{&x} + case token.FLOAT: + var y big.Rat + _, ok := y.SetString(lit) + assert(ok) + return Const{&y} + case token.IMAG: + assert(lit[len(lit)-1] == 'i') + var im big.Rat + _, ok := im.SetString(lit[0 : len(lit)-1]) + assert(ok) + return Const{cmplx{big.NewRat(0, 1), &im}} + case token.CHAR: + assert(lit[0] == '\'' && lit[len(lit)-1] == '\'') + code, _, _, err := strconv.UnquoteChar(lit[1:len(lit)-1], '\'') + assert(err == nil) + return Const{big.NewInt(int64(code))} + case token.STRING: + s, err := strconv.Unquote(lit) + assert(err == nil) + return Const{s} + } + panic("unreachable") +} + +// MakeZero returns the zero constant for the given type. +func MakeZero(typ *Type) Const { + // TODO(gri) fix this + return Const{0} +} + +// Match attempts to match the internal constant representations of x and y. +// If the attempt is successful, the result is the values of x and y, +// if necessary converted to have the same internal representation; otherwise +// the results are invalid. +func (x Const) Match(y Const) (u, v Const) { + switch a := x.val.(type) { + case bool: + if _, ok := y.val.(bool); ok { + u, v = x, y + } + case *big.Int: + switch y.val.(type) { + case *big.Int: + u, v = x, y + case *big.Rat: + var z big.Rat + z.SetInt(a) + u, v = Const{&z}, y + case cmplx: + var z big.Rat + z.SetInt(a) + u, v = Const{cmplx{&z, big.NewRat(0, 1)}}, y + } + case *big.Rat: + switch y.val.(type) { + case *big.Int: + v, u = y.Match(x) + case *big.Rat: + u, v = x, y + case cmplx: + u, v = Const{cmplx{a, big.NewRat(0, 0)}}, y + } + case cmplx: + switch y.val.(type) { + case *big.Int, *big.Rat: + v, u = y.Match(x) + case cmplx: + u, v = x, y + } + case string: + if _, ok := y.val.(string); ok { + u, v = x, y + } + default: + panic("unreachable") + } + return +} + +// Convert attempts to convert the constant x to a given type. +// If the attempt is successful, the result is the new constant; +// otherwise the result is invalid. +func (x Const) Convert(typ *Type) Const { + // TODO(gri) implement this + switch x := x.val.(type) { + case bool: + case *big.Int: + case *big.Rat: + case cmplx: + case string: + } + return x +} + +func (x Const) String() string { + switch x := x.val.(type) { + case bool: + if x { + return "true" + } + return "false" + case *big.Int: + return x.String() + case *big.Rat: + return x.FloatString(10) // 10 digits of precision after decimal point seems fine + case cmplx: + // TODO(gri) don't print 0 components + return x.re.FloatString(10) + " + " + x.im.FloatString(10) + "i" + case string: + return x + } + panic("unreachable") +} + +func (x Const) UnaryOp(op token.Token) Const { + panic("unimplemented") +} + +func (x Const) BinaryOp(op token.Token, y Const) Const { + var z interface{} + switch x := x.val.(type) { + case bool: + z = binaryBoolOp(x, op, y.val.(bool)) + case *big.Int: + z = binaryIntOp(x, op, y.val.(*big.Int)) + case *big.Rat: + z = binaryFloatOp(x, op, y.val.(*big.Rat)) + case cmplx: + z = binaryCmplxOp(x, op, y.val.(cmplx)) + case string: + z = binaryStringOp(x, op, y.val.(string)) + default: + panic("unreachable") + } + return Const{z} +} + +func binaryBoolOp(x bool, op token.Token, y bool) interface{} { + switch op { + case token.EQL: + return x == y + case token.NEQ: + return x != y + } + panic("unreachable") +} + +func binaryIntOp(x *big.Int, op token.Token, y *big.Int) interface{} { + var z big.Int + switch op { + case token.ADD: + return z.Add(x, y) + case token.SUB: + return z.Sub(x, y) + case token.MUL: + return z.Mul(x, y) + case token.QUO: + return z.Quo(x, y) + case token.REM: + return z.Rem(x, y) + case token.AND: + return z.And(x, y) + case token.OR: + return z.Or(x, y) + case token.XOR: + return z.Xor(x, y) + case token.AND_NOT: + return z.AndNot(x, y) + case token.SHL: + panic("unimplemented") + case token.SHR: + panic("unimplemented") + case token.EQL: + return x.Cmp(y) == 0 + case token.NEQ: + return x.Cmp(y) != 0 + case token.LSS: + return x.Cmp(y) < 0 + case token.LEQ: + return x.Cmp(y) <= 0 + case token.GTR: + return x.Cmp(y) > 0 + case token.GEQ: + return x.Cmp(y) >= 0 + } + panic("unreachable") +} + +func binaryFloatOp(x *big.Rat, op token.Token, y *big.Rat) interface{} { + var z big.Rat + switch op { + case token.ADD: + return z.Add(x, y) + case token.SUB: + return z.Sub(x, y) + case token.MUL: + return z.Mul(x, y) + case token.QUO: + return z.Quo(x, y) + case token.EQL: + return x.Cmp(y) == 0 + case token.NEQ: + return x.Cmp(y) != 0 + case token.LSS: + return x.Cmp(y) < 0 + case token.LEQ: + return x.Cmp(y) <= 0 + case token.GTR: + return x.Cmp(y) > 0 + case token.GEQ: + return x.Cmp(y) >= 0 + } + panic("unreachable") +} + +func binaryCmplxOp(x cmplx, op token.Token, y cmplx) interface{} { + a, b := x.re, x.im + c, d := y.re, y.im + switch op { + case token.ADD: + // (a+c) + i(b+d) + var re, im big.Rat + re.Add(a, c) + im.Add(b, d) + return cmplx{&re, &im} + case token.SUB: + // (a-c) + i(b-d) + var re, im big.Rat + re.Sub(a, c) + im.Sub(b, d) + return cmplx{&re, &im} + case token.MUL: + // (ac-bd) + i(bc+ad) + var ac, bd, bc, ad big.Rat + ac.Mul(a, c) + bd.Mul(b, d) + bc.Mul(b, c) + ad.Mul(a, d) + var re, im big.Rat + re.Sub(&ac, &bd) + im.Add(&bc, &ad) + return cmplx{&re, &im} + case token.QUO: + // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd + var ac, bd, bc, ad, s big.Rat + ac.Mul(a, c) + bd.Mul(b, d) + bc.Mul(b, c) + ad.Mul(a, d) + s.Add(c.Mul(c, c), d.Mul(d, d)) + var re, im big.Rat + re.Add(&ac, &bd) + re.Quo(&re, &s) + im.Sub(&bc, &ad) + im.Quo(&im, &s) + return cmplx{&re, &im} + case token.EQL: + return a.Cmp(c) == 0 && b.Cmp(d) == 0 + case token.NEQ: + return a.Cmp(c) != 0 || b.Cmp(d) != 0 + } + panic("unreachable") +} + +func binaryStringOp(x string, op token.Token, y string) interface{} { + switch op { + case token.ADD: + return x + y + case token.EQL: + return x == y + case token.NEQ: + return x != y + case token.LSS: + return x < y + case token.LEQ: + return x <= y + case token.GTR: + return x > y + case token.GEQ: + return x >= y + } + panic("unreachable") +} diff --git a/libgo/go/exp/types/exportdata.go b/libgo/go/exp/types/exportdata.go new file mode 100644 index 00000000000..383520320f4 --- /dev/null +++ b/libgo/go/exp/types/exportdata.go @@ -0,0 +1,132 @@ +// 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. + +// This file implements ExportData. + +package types + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" +) + +func readGopackHeader(buf *bufio.Reader) (name string, size int, err os.Error) { + // See $GOROOT/include/ar.h. + hdr := make([]byte, 64+12+6+6+8+10+2) + _, err = io.ReadFull(buf, hdr) + if err != nil { + return + } + if trace { + fmt.Printf("header: %s", hdr) + } + s := strings.TrimSpace(string(hdr[64+12+6+6+8:][:10])) + size, err = strconv.Atoi(s) + if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { + err = os.NewError("invalid archive header") + return + } + name = strings.TrimSpace(string(hdr[:64])) + return +} + +type dataReader struct { + *bufio.Reader + io.Closer +} + +// ExportData returns a readCloser positioned at the beginning of the +// export data section of the given object/archive file, or an error. +// It is the caller's responsibility to close the readCloser. +// +func ExportData(filename string) (rc io.ReadCloser, err os.Error) { + file, err := os.Open(filename) + if err != nil { + return + } + + defer func() { + if err != nil { + file.Close() + // Add file name to error. + err = fmt.Errorf("reading export data: %s: %v", filename, err) + } + }() + + buf := bufio.NewReader(file) + + // Read first line to make sure this is an object file. + line, err := buf.ReadSlice('\n') + if err != nil { + return + } + if string(line) == "!<arch>\n" { + // Archive file. Scan to __.PKGDEF, which should + // be second archive entry. + var name string + var size int + + // First entry should be __.SYMDEF. + // Read and discard. + if name, size, err = readGopackHeader(buf); err != nil { + return + } + if name != "__.SYMDEF" { + err = os.NewError("go archive does not begin with __.SYMDEF") + return + } + const block = 4096 + tmp := make([]byte, block) + for size > 0 { + n := size + if n > block { + n = block + } + _, err = io.ReadFull(buf, tmp[:n]) + if err != nil { + return + } + size -= n + } + + // Second entry should be __.PKGDEF. + if name, size, err = readGopackHeader(buf); err != nil { + return + } + if name != "__.PKGDEF" { + err = os.NewError("go archive is missing __.PKGDEF") + return + } + + // Read first line of __.PKGDEF data, so that line + // is once again the first line of the input. + line, err = buf.ReadSlice('\n') + if err != nil { + return + } + } + + // Now at __.PKGDEF in archive or still at beginning of file. + // Either way, line should begin with "go object ". + if !strings.HasPrefix(string(line), "go object ") { + err = os.NewError("not a go object file") + return + } + + // Skip over object header to export data. + // Begins after first line with $$. + for line[0] != '$' { + line, err = buf.ReadSlice('\n') + if err != nil { + return + } + } + + rc = &dataReader{buf, file} + return +} diff --git a/libgo/go/exp/types/gcimporter.go b/libgo/go/exp/types/gcimporter.go new file mode 100644 index 00000000000..fe90f910807 --- /dev/null +++ b/libgo/go/exp/types/gcimporter.go @@ -0,0 +1,823 @@ +// 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. + +// This file implements an ast.Importer for gc generated object files. +// TODO(gri) Eventually move this into a separate package outside types. + +package types + +import ( + "big" + "fmt" + "go/ast" + "go/token" + "io" + "os" + "path/filepath" + "runtime" + "scanner" + "strconv" +) + +const trace = false // set to true for debugging + +var ( + pkgRoot = filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH) + pkgExts = [...]string{".a", ".5", ".6", ".8"} +) + +// findPkg returns the filename and package id for an import path. +// If no file was found, an empty filename is returned. +func findPkg(path string) (filename, id string) { + if len(path) == 0 { + return + } + + id = path + var noext string + switch path[0] { + default: + // "x" -> "$GOROOT/pkg/$GOOS_$GOARCH/x.ext", "x" + noext = filepath.Join(pkgRoot, path) + + case '.': + // "./x" -> "/this/directory/x.ext", "/this/directory/x" + cwd, err := os.Getwd() + if err != nil { + return + } + noext = filepath.Join(cwd, path) + id = noext + + case '/': + // "/x" -> "/x.ext", "/x" + noext = path + } + + // try extensions + for _, ext := range pkgExts { + filename = noext + ext + if f, err := os.Stat(filename); err == nil && f.IsRegular() { + return + } + } + + filename = "" // not found + return +} + +// gcParser parses the exports inside a gc compiler-produced +// object/archive file and populates its scope with the results. +type gcParser struct { + scanner scanner.Scanner + tok int // current token + lit string // literal string; only valid for Ident, Int, String tokens + id string // package id of imported package + imports map[string]*ast.Object // package id -> package object +} + +func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*ast.Object) { + p.scanner.Init(src) + p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } + p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments + p.scanner.Whitespace = 1<<'\t' | 1<<' ' + p.scanner.Filename = filename // for good error messages + p.next() + p.id = id + p.imports = imports +} + +func (p *gcParser) next() { + p.tok = p.scanner.Scan() + switch p.tok { + case scanner.Ident, scanner.Int, scanner.String: + p.lit = p.scanner.TokenText() + default: + p.lit = "" + } + if trace { + fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit) + } +} + +// GcImporter implements the ast.Importer signature. +func GcImporter(imports map[string]*ast.Object, path string) (pkg *ast.Object, err os.Error) { + if path == "unsafe" { + return Unsafe, nil + } + + defer func() { + if r := recover(); r != nil { + err = r.(importError) // will re-panic if r is not an importError + if trace { + panic(err) // force a stack trace + } + } + }() + + filename, id := findPkg(path) + if filename == "" { + err = os.NewError("can't find import: " + id) + return + } + + if pkg = imports[id]; pkg != nil { + return // package was imported before + } + + buf, err := ExportData(filename) + if err != nil { + return + } + defer buf.Close() + + if trace { + fmt.Printf("importing %s (%s)\n", id, filename) + } + + var p gcParser + p.init(filename, id, buf, imports) + pkg = p.parseExport() + return +} + +// Declare inserts a named object of the given kind in scope. +func (p *gcParser) declare(scope *ast.Scope, kind ast.ObjKind, name string) *ast.Object { + // a type may have been declared before - if it exists + // already in the respective package scope, return that + // type + if kind == ast.Typ { + if obj := scope.Lookup(name); obj != nil { + assert(obj.Kind == ast.Typ) + return obj + } + } + + // any other object must be a newly declared object - + // create it and insert it into the package scope + obj := ast.NewObj(kind, name) + if scope.Insert(obj) != nil { + p.errorf("already declared: %v %s", kind, obj.Name) + } + + // a new type object is a named type and may be referred + // to before the underlying type is known - set it up + if kind == ast.Typ { + obj.Type = &Name{Obj: obj} + } + + return obj +} + +// ---------------------------------------------------------------------------- +// Error handling + +// Internal errors are boxed as importErrors. +type importError struct { + pos scanner.Position + err os.Error +} + +func (e importError) String() string { + return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) +} + +func (p *gcParser) error(err interface{}) { + if s, ok := err.(string); ok { + err = os.NewError(s) + } + // panic with a runtime.Error if err is not an os.Error + panic(importError{p.scanner.Pos(), err.(os.Error)}) +} + +func (p *gcParser) errorf(format string, args ...interface{}) { + p.error(fmt.Sprintf(format, args...)) +} + +func (p *gcParser) expect(tok int) string { + lit := p.lit + if p.tok != tok { + p.errorf("expected %q, got %q (%q)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) + } + p.next() + return lit +} + +func (p *gcParser) expectSpecial(tok string) { + sep := 'x' // not white space + i := 0 + for i < len(tok) && p.tok == int(tok[i]) && sep > ' ' { + sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token + p.next() + i++ + } + if i < len(tok) { + p.errorf("expected %q, got %q", tok, tok[0:i]) + } +} + +func (p *gcParser) expectKeyword(keyword string) { + lit := p.expect(scanner.Ident) + if lit != keyword { + p.errorf("expected keyword %s, got %q", keyword, lit) + } +} + +// ---------------------------------------------------------------------------- +// Import declarations + +// ImportPath = string_lit . +// +func (p *gcParser) parsePkgId() *ast.Object { + id, err := strconv.Unquote(p.expect(scanner.String)) + if err != nil { + p.error(err) + } + + switch id { + case "": + // id == "" stands for the imported package id + // (only known at time of package installation) + id = p.id + case "unsafe": + // package unsafe is not in the imports map - handle explicitly + return Unsafe + } + + pkg := p.imports[id] + if pkg == nil { + scope = ast.NewScope(nil) + pkg = ast.NewObj(ast.Pkg, "") + pkg.Data = scope + p.imports[id] = pkg + } + + return pkg +} + +// dotIdentifier = ( ident | '·' ) { ident | int | '·' } . +func (p *gcParser) parseDotIdent() string { + ident := "" + if p.tok != scanner.Int { + sep := 'x' // not white space + for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' { + ident += p.lit + sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token + p.next() + } + } + if ident == "" { + p.expect(scanner.Ident) // use expect() for error handling + } + return ident +} + +// ExportedName = "@" ImportPath "." dotIdentifier . +// +func (p *gcParser) parseExportedName() (*ast.Object, string) { + p.expect('@') + pkg := p.parsePkgId() + p.expect('.') + name := p.parseDotIdent() + return pkg, name +} + +// ---------------------------------------------------------------------------- +// Types + +// BasicType = identifier . +// +func (p *gcParser) parseBasicType() Type { + obj := Universe.Lookup(p.expect(scanner.Ident)) + if obj == nil || obj.Kind != ast.Typ { + p.errorf("not a basic type: %s", obj.Name) + } + return obj.Type.(Type) +} + +// ArrayType = "[" int_lit "]" Type . +// +func (p *gcParser) parseArrayType() Type { + // "[" already consumed and lookahead known not to be "]" + lit := p.expect(scanner.Int) + p.expect(']') + elt := p.parseType() + n, err := strconv.Atoui64(lit) + if err != nil { + p.error(err) + } + return &Array{Len: n, Elt: elt} +} + +// MapType = "map" "[" Type "]" Type . +// +func (p *gcParser) parseMapType() Type { + p.expectKeyword("map") + p.expect('[') + key := p.parseType() + p.expect(']') + elt := p.parseType() + return &Map{Key: key, Elt: elt} +} + +// Name = identifier | "?" . +// +func (p *gcParser) parseName() (name string) { + switch p.tok { + case scanner.Ident: + name = p.lit + p.next() + case '?': + // anonymous + p.next() + default: + p.error("name expected") + } + return +} + +// Field = Name Type [ string_lit ] . +// +func (p *gcParser) parseField() (fld *ast.Object, tag string) { + name := p.parseName() + ftyp := p.parseType() + if name == "" { + // anonymous field - ftyp must be T or *T and T must be a type name + if _, ok := Deref(ftyp).(*Name); !ok { + p.errorf("anonymous field expected") + } + } + if p.tok == scanner.String { + tag = p.expect(scanner.String) + } + fld = ast.NewObj(ast.Var, name) + fld.Type = ftyp + return +} + +// StructType = "struct" "{" [ FieldList ] "}" . +// FieldList = Field { ";" Field } . +// +func (p *gcParser) parseStructType() Type { + var fields []*ast.Object + var tags []string + + parseField := func() { + fld, tag := p.parseField() + fields = append(fields, fld) + tags = append(tags, tag) + } + + p.expectKeyword("struct") + p.expect('{') + if p.tok != '}' { + parseField() + for p.tok == ';' { + p.next() + parseField() + } + } + p.expect('}') + + return &Struct{Fields: fields, Tags: tags} +} + +// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . +// +func (p *gcParser) parseParameter() (par *ast.Object, isVariadic bool) { + name := p.parseName() + if name == "" { + name = "_" // cannot access unnamed identifiers + } + if p.tok == '.' { + p.expectSpecial("...") + isVariadic = true + } + ptyp := p.parseType() + // ignore argument tag + if p.tok == scanner.String { + p.expect(scanner.String) + } + par = ast.NewObj(ast.Var, name) + par.Type = ptyp + return +} + +// Parameters = "(" [ ParameterList ] ")" . +// ParameterList = { Parameter "," } Parameter . +// +func (p *gcParser) parseParameters() (list []*ast.Object, isVariadic bool) { + parseParameter := func() { + par, variadic := p.parseParameter() + list = append(list, par) + if variadic { + if isVariadic { + p.error("... not on final argument") + } + isVariadic = true + } + } + + p.expect('(') + if p.tok != ')' { + parseParameter() + for p.tok == ',' { + p.next() + parseParameter() + } + } + p.expect(')') + + return +} + +// Signature = Parameters [ Result ] . +// Result = Type | Parameters . +// +func (p *gcParser) parseSignature() *Func { + params, isVariadic := p.parseParameters() + + // optional result type + var results []*ast.Object + switch p.tok { + case scanner.Ident, '[', '*', '<', '@': + // single, unnamed result + result := ast.NewObj(ast.Var, "_") + result.Type = p.parseType() + results = []*ast.Object{result} + case '(': + // named or multiple result(s) + var variadic bool + results, variadic = p.parseParameters() + if variadic { + p.error("... not permitted on result type") + } + } + + return &Func{Params: params, Results: results, IsVariadic: isVariadic} +} + +// MethodSpec = ( identifier | ExportedName ) Signature . +// +func (p *gcParser) parseMethodSpec() *ast.Object { + if p.tok == scanner.Ident { + p.expect(scanner.Ident) + } else { + p.parseExportedName() + } + p.parseSignature() + + // TODO(gri) compute method object + return ast.NewObj(ast.Fun, "_") +} + +// InterfaceType = "interface" "{" [ MethodList ] "}" . +// MethodList = MethodSpec { ";" MethodSpec } . +// +func (p *gcParser) parseInterfaceType() Type { + var methods ObjList + + parseMethod := func() { + meth := p.parseMethodSpec() + methods = append(methods, meth) + } + + p.expectKeyword("interface") + p.expect('{') + if p.tok != '}' { + parseMethod() + for p.tok == ';' { + p.next() + parseMethod() + } + } + p.expect('}') + + methods.Sort() + return &Interface{Methods: methods} +} + +// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . +// +func (p *gcParser) parseChanType() Type { + dir := ast.SEND | ast.RECV + if p.tok == scanner.Ident { + p.expectKeyword("chan") + if p.tok == '<' { + p.expectSpecial("<-") + dir = ast.SEND + } + } else { + p.expectSpecial("<-") + p.expectKeyword("chan") + dir = ast.RECV + } + elt := p.parseType() + return &Chan{Dir: dir, Elt: elt} +} + +// Type = +// BasicType | TypeName | ArrayType | SliceType | StructType | +// PointerType | FuncType | InterfaceType | MapType | ChanType | +// "(" Type ")" . +// BasicType = ident . +// TypeName = ExportedName . +// SliceType = "[" "]" Type . +// PointerType = "*" Type . +// FuncType = "func" Signature . +// +func (p *gcParser) parseType() Type { + switch p.tok { + case scanner.Ident: + switch p.lit { + default: + return p.parseBasicType() + case "struct": + return p.parseStructType() + case "func": + // FuncType + p.next() + return p.parseSignature() + case "interface": + return p.parseInterfaceType() + case "map": + return p.parseMapType() + case "chan": + return p.parseChanType() + } + case '@': + // TypeName + pkg, name := p.parseExportedName() + return p.declare(pkg.Data.(*ast.Scope), ast.Typ, name).Type.(Type) + case '[': + p.next() // look ahead + if p.tok == ']' { + // SliceType + p.next() + return &Slice{Elt: p.parseType()} + } + return p.parseArrayType() + case '*': + // PointerType + p.next() + return &Pointer{Base: p.parseType()} + case '<': + return p.parseChanType() + case '(': + // "(" Type ")" + p.next() + typ := p.parseType() + p.expect(')') + return typ + } + p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit) + return nil +} + +// ---------------------------------------------------------------------------- +// Declarations + +// ImportDecl = "import" identifier string_lit . +// +func (p *gcParser) parseImportDecl() { + p.expectKeyword("import") + // The identifier has no semantic meaning in the import data. + // It exists so that error messages can print the real package + // name: binary.ByteOrder instead of "encoding/binary".ByteOrder. + name := p.expect(scanner.Ident) + pkg := p.parsePkgId() + assert(pkg.Name == "" || pkg.Name == name) + pkg.Name = name +} + +// int_lit = [ "+" | "-" ] { "0" ... "9" } . +// +func (p *gcParser) parseInt() (sign, val string) { + switch p.tok { + case '-': + p.next() + sign = "-" + case '+': + p.next() + } + val = p.expect(scanner.Int) + return +} + +// number = int_lit [ "p" int_lit ] . +// +func (p *gcParser) parseNumber() Const { + // mantissa + sign, val := p.parseInt() + mant, ok := new(big.Int).SetString(sign+val, 10) + assert(ok) + + if p.lit == "p" { + // exponent (base 2) + p.next() + sign, val = p.parseInt() + exp, err := strconv.Atoui(val) + if err != nil { + p.error(err) + } + if sign == "-" { + denom := big.NewInt(1) + denom.Lsh(denom, exp) + return Const{new(big.Rat).SetFrac(mant, denom)} + } + if exp > 0 { + mant.Lsh(mant, exp) + } + return Const{new(big.Rat).SetInt(mant)} + } + + return Const{mant} +} + +// ConstDecl = "const" ExportedName [ Type ] "=" Literal . +// Literal = bool_lit | int_lit | float_lit | complex_lit | string_lit . +// bool_lit = "true" | "false" . +// complex_lit = "(" float_lit "+" float_lit ")" . +// string_lit = `"` { unicode_char } `"` . +// +func (p *gcParser) parseConstDecl() { + p.expectKeyword("const") + pkg, name := p.parseExportedName() + obj := p.declare(pkg.Data.(*ast.Scope), ast.Con, name) + var x Const + var typ Type + if p.tok != '=' { + obj.Type = p.parseType() + } + p.expect('=') + switch p.tok { + case scanner.Ident: + // bool_lit + if p.lit != "true" && p.lit != "false" { + p.error("expected true or false") + } + x = Const{p.lit == "true"} + typ = Bool.Underlying + p.next() + case '-', scanner.Int: + // int_lit + x = p.parseNumber() + typ = Int.Underlying + if _, ok := x.val.(*big.Rat); ok { + typ = Float64.Underlying + } + case '(': + // complex_lit + p.next() + re := p.parseNumber() + p.expect('+') + im := p.parseNumber() + p.expect(')') + x = Const{cmplx{re.val.(*big.Rat), im.val.(*big.Rat)}} + typ = Complex128.Underlying + case scanner.String: + // string_lit + x = MakeConst(token.STRING, p.lit) + p.next() + typ = String.Underlying + default: + p.error("expected literal") + } + if obj.Type == nil { + obj.Type = typ + } + obj.Data = x +} + +// TypeDecl = "type" ExportedName Type . +// +func (p *gcParser) parseTypeDecl() { + p.expectKeyword("type") + pkg, name := p.parseExportedName() + obj := p.declare(pkg.Data.(*ast.Scope), ast.Typ, name) + + // The type object may have been imported before and thus already + // have a type associated with it. We still need to parse the type + // structure, but throw it away if the object already has a type. + // This ensures that all imports refer to the same type object for + // a given type declaration. + typ := p.parseType() + + if name := obj.Type.(*Name); name.Underlying == nil { + assert(Underlying(typ) == typ) + name.Underlying = typ + } +} + +// VarDecl = "var" ExportedName Type . +// +func (p *gcParser) parseVarDecl() { + p.expectKeyword("var") + pkg, name := p.parseExportedName() + obj := p.declare(pkg.Data.(*ast.Scope), ast.Var, name) + obj.Type = p.parseType() +} + +// FuncBody = "{" ... "}" . +// +func (p *gcParser) parseFuncBody() { + p.expect('{') + for i := 1; i > 0; p.next() { + switch p.tok { + case '{': + i++ + case '}': + i-- + } + } +} + +// FuncDecl = "func" ExportedName Signature [ FuncBody ] . +// +func (p *gcParser) parseFuncDecl() { + // "func" already consumed + pkg, name := p.parseExportedName() + obj := p.declare(pkg.Data.(*ast.Scope), ast.Fun, name) + obj.Type = p.parseSignature() + if p.tok == '{' { + p.parseFuncBody() + } +} + +// MethodDecl = "func" Receiver identifier Signature . +// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" [ FuncBody ]. +// +func (p *gcParser) parseMethodDecl() { + // "func" already consumed + p.expect('(') + p.parseParameter() // receiver + p.expect(')') + p.expect(scanner.Ident) + p.parseSignature() + if p.tok == '{' { + p.parseFuncBody() + } +} + +// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . +// +func (p *gcParser) parseDecl() { + switch p.lit { + case "import": + p.parseImportDecl() + case "const": + p.parseConstDecl() + case "type": + p.parseTypeDecl() + case "var": + p.parseVarDecl() + case "func": + p.next() // look ahead + if p.tok == '(' { + p.parseMethodDecl() + } else { + p.parseFuncDecl() + } + } + p.expect('\n') +} + +// ---------------------------------------------------------------------------- +// Export + +// Export = "PackageClause { Decl } "$$" . +// PackageClause = "package" identifier [ "safe" ] "\n" . +// +func (p *gcParser) parseExport() *ast.Object { + p.expectKeyword("package") + name := p.expect(scanner.Ident) + if p.tok != '\n' { + // A package is safe if it was compiled with the -u flag, + // which disables the unsafe package. + // TODO(gri) remember "safe" package + p.expectKeyword("safe") + } + p.expect('\n') + + assert(p.imports[p.id] == nil) + pkg := ast.NewObj(ast.Pkg, name) + pkg.Data = ast.NewScope(nil) + p.imports[p.id] = pkg + + for p.tok != '$' && p.tok != scanner.EOF { + p.parseDecl() + } + + if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' { + // don't call next()/expect() since reading past the + // export data may cause scanner errors (e.g. NUL chars) + p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch) + } + + if n := p.scanner.ErrorCount; n != 0 { + p.errorf("expected no scanner errors, got %d", n) + } + + return pkg +} diff --git a/libgo/go/exp/types/gcimporter_test.go b/libgo/go/exp/types/gcimporter_test.go new file mode 100644 index 00000000000..ec87f5d514b --- /dev/null +++ b/libgo/go/exp/types/gcimporter_test.go @@ -0,0 +1,101 @@ +// 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 types + +import ( + "exec" + "go/ast" + "io/ioutil" + "path/filepath" + "runtime" + "strings" + "testing" + "time" +) + +var gcName, gcPath string // compiler name and path + +func init() { + // determine compiler + switch runtime.GOARCH { + case "386": + gcName = "8g" + case "amd64": + gcName = "6g" + case "arm": + gcName = "5g" + default: + gcName = "unknown-GOARCH-compiler" + gcPath = gcName + return + } + gcPath, _ = exec.LookPath(gcName) +} + +func compile(t *testing.T, dirname, filename string) { + cmd := exec.Command(gcPath, filename) + cmd.Dir = dirname + out, err := cmd.CombinedOutput() + if err != nil { + t.Errorf("%s %s failed: %s", gcName, filename, err) + return + } + t.Logf("%s", string(out)) +} + +// Use the same global imports map for all tests. The effect is +// as if all tested packages were imported into a single package. +var imports = make(map[string]*ast.Object) + +func testPath(t *testing.T, path string) bool { + _, err := GcImporter(imports, path) + if err != nil { + t.Errorf("testPath(%s): %s", path, err) + return false + } + return true +} + +const maxTime = 3e9 // maximum allotted testing time in ns + +func testDir(t *testing.T, dir string, endTime int64) (nimports int) { + dirname := filepath.Join(pkgRoot, dir) + list, err := ioutil.ReadDir(dirname) + if err != nil { + t.Errorf("testDir(%s): %s", dirname, err) + } + for _, f := range list { + if time.Nanoseconds() >= endTime { + t.Log("testing time used up") + return + } + switch { + case f.IsRegular(): + // try extensions + for _, ext := range pkgExts { + if strings.HasSuffix(f.Name, ext) { + name := f.Name[0 : len(f.Name)-len(ext)] // remove extension + if testPath(t, filepath.Join(dir, name)) { + nimports++ + } + } + } + case f.IsDirectory(): + nimports += testDir(t, filepath.Join(dir, f.Name), endTime) + } + } + return +} + +func TestGcImport(t *testing.T) { + compile(t, "testdata", "exports.go") + + nimports := 0 + if testPath(t, "./testdata/exports") { + nimports++ + } + nimports += testDir(t, "", time.Nanoseconds()+maxTime) // installed packages + t.Logf("tested %d imports", nimports) +} diff --git a/libgo/go/exp/types/testdata/exports.go b/libgo/go/exp/types/testdata/exports.go new file mode 100644 index 00000000000..ed63bf9adec --- /dev/null +++ b/libgo/go/exp/types/testdata/exports.go @@ -0,0 +1,84 @@ +// 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. + +// This file is used to generate an object file which +// serves as test file for gcimporter_test.go. + +package exports + +import ( + "go/ast" +) + +const ( + C0 int = 0 + C1 = 3.14159265 + C2 = 2.718281828i + C3 = -123.456e-789 + C4 = +123.456E+789 + C5 = 1234i + C6 = "foo\n" + C7 = `bar\n` +) + +type ( + T1 int + T2 [10]int + T3 []int + T4 *int + T5 chan int + T6a chan<- int + T6b chan (<-chan int) + T6c chan<- (chan int) + T7 <-chan *ast.File + T8 struct{} + T9 struct { + a int + b, c float32 + d []string `go:"tag"` + } + T10 struct { + T8 + T9 + _ *T10 + } + T11 map[int]string + T12 interface{} + T13 interface { + m1() + m2(int) float32 + } + T14 interface { + T12 + T13 + m3(x ...struct{}) []T9 + } + T15 func() + T16 func(int) + T17 func(x int) + T18 func() float32 + T19 func() (x float32) + T20 func(...interface{}) + T21 struct{ next *T21 } + T22 struct{ link *T23 } + T23 struct{ link *T22 } + T24 *T24 + T25 *T26 + T26 *T27 + T27 *T25 + T28 func(T28) T28 +) + +var ( + V0 int + V1 = -991.0 +) + +func F1() {} +func F2(x int) {} +func F3() int { return 0 } +func F4() float32 { return 0 } +func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10) + +func (p *T1) M1() diff --git a/libgo/go/exp/types/types.go b/libgo/go/exp/types/types.go new file mode 100644 index 00000000000..3aa896892e3 --- /dev/null +++ b/libgo/go/exp/types/types.go @@ -0,0 +1,255 @@ +// 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 UNDER CONSTRUCTION. ANY AND ALL PARTS MAY CHANGE. +// Package types declares the types used to represent Go types. +// +package types + +import ( + "go/ast" + "sort" +) + +// All types implement the Type interface. +type Type interface { + isType() +} + +// All concrete types embed ImplementsType which +// ensures that all types implement the Type interface. +type ImplementsType struct{} + +func (t *ImplementsType) isType() {} + +// A Bad type is a non-nil placeholder type when we don't know a type. +type Bad struct { + ImplementsType + Msg string // for better error reporting/debugging +} + +// A Basic represents a (unnamed) basic type. +type Basic struct { + ImplementsType + // TODO(gri) need a field specifying the exact basic type +} + +// An Array represents an array type [Len]Elt. +type Array struct { + ImplementsType + Len uint64 + Elt Type +} + +// A Slice represents a slice type []Elt. +type Slice struct { + ImplementsType + Elt Type +} + +// A Struct represents a struct type struct{...}. +// Anonymous fields are represented by objects with empty names. +type Struct struct { + ImplementsType + Fields ObjList // struct fields; or nil + Tags []string // corresponding tags; or nil + // TODO(gri) This type needs some rethinking: + // - at the moment anonymous fields are marked with "" object names, + // and their names have to be reconstructed + // - there is no scope for fast lookup (but the parser creates one) +} + +// A Pointer represents a pointer type *Base. +type Pointer struct { + ImplementsType + Base Type +} + +// A Func represents a function type func(...) (...). +// Unnamed parameters are represented by objects with empty names. +type Func struct { + ImplementsType + Recv *ast.Object // nil if not a method + Params ObjList // (incoming) parameters from left to right; or nil + Results ObjList // (outgoing) results from left to right; or nil + IsVariadic bool // true if the last parameter's type is of the form ...T +} + +// An Interface represents an interface type interface{...}. +type Interface struct { + ImplementsType + Methods ObjList // interface methods sorted by name; or nil +} + +// A Map represents a map type map[Key]Elt. +type Map struct { + ImplementsType + Key, Elt Type +} + +// A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt. +type Chan struct { + ImplementsType + Dir ast.ChanDir + Elt Type +} + +// A Name represents a named type as declared in a type declaration. +type Name struct { + ImplementsType + Underlying Type // nil if not fully declared + Obj *ast.Object // corresponding declared object + // TODO(gri) need to remember fields and methods. +} + +// If typ is a pointer type, Deref returns the pointer's base type; +// otherwise it returns typ. +func Deref(typ Type) Type { + if typ, ok := typ.(*Pointer); ok { + return typ.Base + } + return typ +} + +// Underlying returns the underlying type of a type. +func Underlying(typ Type) Type { + if typ, ok := typ.(*Name); ok { + utyp := typ.Underlying + if _, ok := utyp.(*Basic); !ok { + return utyp + } + // the underlying type of a type name referring + // to an (untyped) basic type is the basic type + // name + } + return typ +} + +// An ObjList represents an ordered (in some fashion) list of objects. +type ObjList []*ast.Object + +// ObjList implements sort.Interface. +func (list ObjList) Len() int { return len(list) } +func (list ObjList) Less(i, j int) bool { return list[i].Name < list[j].Name } +func (list ObjList) Swap(i, j int) { list[i], list[j] = list[j], list[i] } + +// Sort sorts an object list by object name. +func (list ObjList) Sort() { sort.Sort(list) } + +// identicalTypes returns true if both lists a and b have the +// same length and corresponding objects have identical types. +func identicalTypes(a, b ObjList) bool { + if len(a) == len(b) { + for i, x := range a { + y := b[i] + if !Identical(x.Type.(Type), y.Type.(Type)) { + return false + } + } + return true + } + return false +} + +// Identical returns true if two types are identical. +func Identical(x, y Type) bool { + if x == y { + return true + } + + switch x := x.(type) { + case *Bad: + // A Bad type is always identical to any other type + // (to avoid spurious follow-up errors). + return true + + case *Basic: + if y, ok := y.(*Basic); ok { + panic("unimplemented") + _ = y + } + + case *Array: + // Two array types are identical if they have identical element types + // and the same array length. + if y, ok := y.(*Array); ok { + return x.Len == y.Len && Identical(x.Elt, y.Elt) + } + + case *Slice: + // Two slice types are identical if they have identical element types. + if y, ok := y.(*Slice); ok { + return Identical(x.Elt, y.Elt) + } + + case *Struct: + // Two struct types are identical if they have the same sequence of fields, + // and if corresponding fields have the same names, and identical types, + // and identical tags. Two anonymous fields are considered to have the same + // name. Lower-case field names from different packages are always different. + if y, ok := y.(*Struct); ok { + // TODO(gri) handle structs from different packages + if identicalTypes(x.Fields, y.Fields) { + for i, f := range x.Fields { + g := y.Fields[i] + if f.Name != g.Name || x.Tags[i] != y.Tags[i] { + return false + } + } + return true + } + } + + case *Pointer: + // Two pointer types are identical if they have identical base types. + if y, ok := y.(*Pointer); ok { + return Identical(x.Base, y.Base) + } + + case *Func: + // Two function types are identical if they have the same number of parameters + // and result values, corresponding parameter and result types are identical, + // and either both functions are variadic or neither is. Parameter and result + // names are not required to match. + if y, ok := y.(*Func); ok { + return identicalTypes(x.Params, y.Params) && + identicalTypes(x.Results, y.Results) && + x.IsVariadic == y.IsVariadic + } + + case *Interface: + // Two interface types are identical if they have the same set of methods with + // the same names and identical function types. Lower-case method names from + // different packages are always different. The order of the methods is irrelevant. + if y, ok := y.(*Interface); ok { + return identicalTypes(x.Methods, y.Methods) // methods are sorted + } + + case *Map: + // Two map types are identical if they have identical key and value types. + if y, ok := y.(*Map); ok { + return Identical(x.Key, y.Key) && Identical(x.Elt, y.Elt) + } + + case *Chan: + // Two channel types are identical if they have identical value types + // and the same direction. + if y, ok := y.(*Chan); ok { + return x.Dir == y.Dir && Identical(x.Elt, y.Elt) + } + + case *Name: + // Two named types are identical if their type names originate + // in the same type declaration. + if y, ok := y.(*Name); ok { + return x.Obj == y.Obj || + // permit bad objects to be equal to avoid + // follow up errors + x.Obj != nil && x.Obj.Kind == ast.Bad || + y.Obj != nil && y.Obj.Kind == ast.Bad + } + } + + return false +} diff --git a/libgo/go/exp/types/universe.go b/libgo/go/exp/types/universe.go new file mode 100644 index 00000000000..80db1278295 --- /dev/null +++ b/libgo/go/exp/types/universe.go @@ -0,0 +1,109 @@ +// 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. + +// FILE UNDER CONSTRUCTION. ANY AND ALL PARTS MAY CHANGE. +// This file implements the universe and unsafe package scopes. + +package types + +import "go/ast" + +var ( + scope *ast.Scope // current scope to use for initialization + Universe *ast.Scope + Unsafe *ast.Object // package unsafe +) + +func define(kind ast.ObjKind, name string) *ast.Object { + obj := ast.NewObj(kind, name) + if scope.Insert(obj) != nil { + panic("types internal error: double declaration") + } + return obj +} + +func defType(name string) *Name { + obj := define(ast.Typ, name) + typ := &Name{Underlying: &Basic{}, Obj: obj} + obj.Type = typ + return typ +} + +func defConst(name string) { + obj := define(ast.Con, name) + _ = obj // TODO(gri) fill in other properties +} + +func defFun(name string) { + obj := define(ast.Fun, name) + _ = obj // TODO(gri) fill in other properties +} + +var ( + Bool, + Int, + Float64, + Complex128, + String *Name +) + +func init() { + scope = ast.NewScope(nil) + Universe = scope + + Bool = defType("bool") + defType("byte") // TODO(gri) should be an alias for uint8 + defType("complex64") + Complex128 = defType("complex128") + defType("float32") + Float64 = defType("float64") + defType("int8") + defType("int16") + defType("int32") + defType("int64") + String = defType("string") + defType("uint8") + defType("uint16") + defType("uint32") + defType("uint64") + Int = defType("int") + defType("uint") + defType("uintptr") + + defConst("true") + defConst("false") + defConst("iota") + defConst("nil") + + defFun("append") + defFun("cap") + defFun("close") + defFun("complex") + defFun("copy") + defFun("delete") + defFun("imag") + defFun("len") + defFun("make") + defFun("new") + defFun("panic") + defFun("print") + defFun("println") + defFun("real") + defFun("recover") + + scope = ast.NewScope(nil) + Unsafe = ast.NewObj(ast.Pkg, "unsafe") + Unsafe.Data = scope + + defType("Pointer") + + defFun("Alignof") + defFun("New") + defFun("NewArray") + defFun("Offsetof") + defFun("Reflect") + defFun("Sizeof") + defFun("Typeof") + defFun("Unreflect") +} diff --git a/libgo/go/exp/winfsnotify/winfsnotify.go b/libgo/go/exp/winfsnotify/winfsnotify.go new file mode 100644 index 00000000000..c5dfe99ad71 --- /dev/null +++ b/libgo/go/exp/winfsnotify/winfsnotify.go @@ -0,0 +1,569 @@ +// 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 winfsnotify allows the user to receive +// file system event notifications on Windows. +package winfsnotify + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "syscall" + "unsafe" +) + +// Event is the type of the notification messages +// received on the watcher's Event channel. +type Event struct { + Mask uint32 // Mask of events + Cookie uint32 // Unique cookie associating related events (for rename) + Name string // File name (optional) +} + +const ( + opAddWatch = iota + opRemoveWatch +) + +const ( + provisional uint64 = 1 << (32 + iota) +) + +type input struct { + op int + path string + flags uint32 + reply chan os.Error +} + +type inode struct { + handle syscall.Handle + volume uint32 + index uint64 +} + +type watch struct { + ov syscall.Overlapped + ino *inode // i-number + path string // Directory path + mask uint64 // Directory itself is being watched with these notify flags + names map[string]uint64 // Map of names being watched and their notify flags + rename string // Remembers the old name while renaming a file + buf [4096]byte +} + +type indexMap map[uint64]*watch +type watchMap map[uint32]indexMap + +// A Watcher waits for and receives event notifications +// for a specific set of files and directories. +type Watcher struct { + port syscall.Handle // Handle to completion port + watches watchMap // Map of watches (key: i-number) + input chan *input // Inputs to the reader are sent on this channel + Event chan *Event // Events are returned on this channel + Error chan os.Error // Errors are sent on this channel + isClosed bool // Set to true when Close() is first called + quit chan chan<- os.Error + cookie uint32 +} + +// NewWatcher creates and returns a Watcher. +func NewWatcher() (*Watcher, os.Error) { + port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0) + if e != 0 { + return nil, os.NewSyscallError("CreateIoCompletionPort", e) + } + w := &Watcher{ + port: port, + watches: make(watchMap), + input: make(chan *input, 1), + Event: make(chan *Event, 50), + Error: make(chan os.Error), + quit: make(chan chan<- os.Error, 1), + } + go w.readEvents() + return w, nil +} + +// Close closes a Watcher. +// It sends a message to the reader goroutine to quit and removes all watches +// associated with the watcher. +func (w *Watcher) Close() os.Error { + if w.isClosed { + return nil + } + w.isClosed = true + + // Send "quit" message to the reader goroutine + ch := make(chan os.Error) + w.quit <- ch + if err := w.wakeupReader(); err != nil { + return err + } + return <-ch +} + +// AddWatch adds path to the watched file set. +func (w *Watcher) AddWatch(path string, flags uint32) os.Error { + if w.isClosed { + return os.NewError("watcher already closed") + } + in := &input{ + op: opAddWatch, + path: filepath.Clean(path), + flags: flags, + reply: make(chan os.Error), + } + w.input <- in + if err := w.wakeupReader(); err != nil { + return err + } + return <-in.reply +} + +// Watch adds path to the watched file set, watching all events. +func (w *Watcher) Watch(path string) os.Error { + return w.AddWatch(path, FS_ALL_EVENTS) +} + +// RemoveWatch removes path from the watched file set. +func (w *Watcher) RemoveWatch(path string) os.Error { + in := &input{ + op: opRemoveWatch, + path: filepath.Clean(path), + reply: make(chan os.Error), + } + w.input <- in + if err := w.wakeupReader(); err != nil { + return err + } + return <-in.reply +} + +func (w *Watcher) wakeupReader() os.Error { + e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil) + if e != 0 { + return os.NewSyscallError("PostQueuedCompletionStatus", e) + } + return nil +} + +func getDir(pathname string) (dir string, err os.Error) { + attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname)) + if e != 0 { + return "", os.NewSyscallError("GetFileAttributes", e) + } + if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + dir = pathname + } else { + dir, _ = filepath.Split(pathname) + dir = filepath.Clean(dir) + } + return +} + +func getIno(path string) (ino *inode, err os.Error) { + h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path), + syscall.FILE_LIST_DIRECTORY, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + nil, syscall.OPEN_EXISTING, + syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0) + if e != 0 { + return nil, os.NewSyscallError("CreateFile", e) + } + var fi syscall.ByHandleFileInformation + if e = syscall.GetFileInformationByHandle(h, &fi); e != 0 { + syscall.CloseHandle(h) + return nil, os.NewSyscallError("GetFileInformationByHandle", e) + } + ino = &inode{ + handle: h, + volume: fi.VolumeSerialNumber, + index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), + } + return ino, nil +} + +// Must run within the I/O thread. +func (m watchMap) get(ino *inode) *watch { + if i := m[ino.volume]; i != nil { + return i[ino.index] + } + return nil +} + +// Must run within the I/O thread. +func (m watchMap) set(ino *inode, watch *watch) { + i := m[ino.volume] + if i == nil { + i = make(indexMap) + m[ino.volume] = i + } + i[ino.index] = watch +} + +// Must run within the I/O thread. +func (w *Watcher) addWatch(pathname string, flags uint64) os.Error { + dir, err := getDir(pathname) + if err != nil { + return err + } + if flags&FS_ONLYDIR != 0 && pathname != dir { + return nil + } + ino, err := getIno(dir) + if err != nil { + return err + } + watchEntry := w.watches.get(ino) + if watchEntry == nil { + if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != 0 { + syscall.CloseHandle(ino.handle) + return os.NewSyscallError("CreateIoCompletionPort", e) + } + watchEntry = &watch{ + ino: ino, + path: dir, + names: make(map[string]uint64), + } + w.watches.set(ino, watchEntry) + flags |= provisional + } else { + syscall.CloseHandle(ino.handle) + } + if pathname == dir { + watchEntry.mask |= flags + } else { + watchEntry.names[filepath.Base(pathname)] |= flags + } + if err = w.startRead(watchEntry); err != nil { + return err + } + if pathname == dir { + watchEntry.mask &= ^provisional + } else { + watchEntry.names[filepath.Base(pathname)] &= ^provisional + } + return nil +} + +// Must run within the I/O thread. +func (w *Watcher) removeWatch(pathname string) os.Error { + dir, err := getDir(pathname) + if err != nil { + return err + } + ino, err := getIno(dir) + if err != nil { + return err + } + watch := w.watches.get(ino) + if watch == nil { + return fmt.Errorf("can't remove non-existent watch for: %s", pathname) + } + if pathname == dir { + w.sendEvent(watch.path, watch.mask&FS_IGNORED) + watch.mask = 0 + } else { + name := filepath.Base(pathname) + w.sendEvent(watch.path+"/"+name, watch.names[name]&FS_IGNORED) + delete(watch.names, name) + } + return w.startRead(watch) +} + +// Must run within the I/O thread. +func (w *Watcher) deleteWatch(watch *watch) { + for name, mask := range watch.names { + if mask&provisional == 0 { + w.sendEvent(watch.path+"/"+name, mask&FS_IGNORED) + } + delete(watch.names, name) + } + if watch.mask != 0 { + if watch.mask&provisional == 0 { + w.sendEvent(watch.path, watch.mask&FS_IGNORED) + } + watch.mask = 0 + } +} + +// Must run within the I/O thread. +func (w *Watcher) startRead(watch *watch) os.Error { + if e := syscall.CancelIo(watch.ino.handle); e != 0 { + w.Error <- os.NewSyscallError("CancelIo", e) + w.deleteWatch(watch) + } + mask := toWindowsFlags(watch.mask) + for _, m := range watch.names { + mask |= toWindowsFlags(m) + } + if mask == 0 { + if e := syscall.CloseHandle(watch.ino.handle); e != 0 { + w.Error <- os.NewSyscallError("CloseHandle", e) + } + delete(w.watches[watch.ino.volume], watch.ino.index) + return nil + } + e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], + uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) + if e != 0 { + err := os.NewSyscallError("ReadDirectoryChanges", e) + if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { + // Watched directory was probably removed + if w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF) { + if watch.mask&FS_ONESHOT != 0 { + watch.mask = 0 + } + } + err = nil + } + w.deleteWatch(watch) + w.startRead(watch) + return err + } + return nil +} + +// readEvents reads from the I/O completion port, converts the +// received events into Event objects and sends them via the Event channel. +// Entry point to the I/O thread. +func (w *Watcher) readEvents() { + var ( + n, key uint32 + ov *syscall.Overlapped + ) + runtime.LockOSThread() + + for { + e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) + watch := (*watch)(unsafe.Pointer(ov)) + + if watch == nil { + select { + case ch := <-w.quit: + for _, index := range w.watches { + for _, watch := range index { + w.deleteWatch(watch) + w.startRead(watch) + } + } + var err os.Error + if e := syscall.CloseHandle(w.port); e != 0 { + err = os.NewSyscallError("CloseHandle", e) + } + close(w.Event) + close(w.Error) + ch <- err + return + case in := <-w.input: + switch in.op { + case opAddWatch: + in.reply <- w.addWatch(in.path, uint64(in.flags)) + case opRemoveWatch: + in.reply <- w.removeWatch(in.path) + } + default: + } + continue + } + + switch e { + case syscall.ERROR_ACCESS_DENIED: + // Watched directory was probably removed + w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF) + w.deleteWatch(watch) + w.startRead(watch) + continue + case syscall.ERROR_OPERATION_ABORTED: + // CancelIo was called on this handle + continue + default: + w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e) + continue + case 0: + } + + var offset uint32 + for { + if n == 0 { + w.Event <- &Event{Mask: FS_Q_OVERFLOW} + w.Error <- os.NewError("short read in readEvents()") + break + } + + // Point "raw" to the event in the buffer + raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) + buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) + name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) + fullname := watch.path + "/" + name + + var mask uint64 + switch raw.Action { + case syscall.FILE_ACTION_REMOVED: + mask = FS_DELETE_SELF + case syscall.FILE_ACTION_MODIFIED: + mask = FS_MODIFY + case syscall.FILE_ACTION_RENAMED_OLD_NAME: + watch.rename = name + case syscall.FILE_ACTION_RENAMED_NEW_NAME: + if watch.names[watch.rename] != 0 { + watch.names[name] |= watch.names[watch.rename] + delete(watch.names, watch.rename) + mask = FS_MOVE_SELF + } + } + + sendNameEvent := func() { + if w.sendEvent(fullname, watch.names[name]&mask) { + if watch.names[name]&FS_ONESHOT != 0 { + delete(watch.names, name) + } + } + } + if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { + sendNameEvent() + } + if raw.Action == syscall.FILE_ACTION_REMOVED { + w.sendEvent(fullname, watch.names[name]&FS_IGNORED) + delete(watch.names, name) + } + if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { + if watch.mask&FS_ONESHOT != 0 { + watch.mask = 0 + } + } + if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { + fullname = watch.path + "/" + watch.rename + sendNameEvent() + } + + // Move to the next event in the buffer + if raw.NextEntryOffset == 0 { + break + } + offset += raw.NextEntryOffset + } + + if err := w.startRead(watch); err != nil { + w.Error <- err + } + } +} + +func (w *Watcher) sendEvent(name string, mask uint64) bool { + if mask == 0 { + return false + } + event := &Event{Mask: uint32(mask), Name: name} + if mask&FS_MOVE != 0 { + if mask&FS_MOVED_FROM != 0 { + w.cookie++ + } + event.Cookie = w.cookie + } + select { + case ch := <-w.quit: + w.quit <- ch + case w.Event <- event: + } + return true +} + +// String formats the event e in the form +// "filename: 0xEventMask = FS_ACCESS|FS_ATTRIB_|..." +func (e *Event) String() string { + var events string + m := e.Mask + for _, b := range eventBits { + if m&b.Value != 0 { + m &^= b.Value + events += "|" + b.Name + } + } + if m != 0 { + events += fmt.Sprintf("|%#x", m) + } + if len(events) > 0 { + events = " == " + events[1:] + } + return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events) +} + +func toWindowsFlags(mask uint64) uint32 { + var m uint32 + if mask&FS_ACCESS != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS + } + if mask&FS_MODIFY != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE + } + if mask&FS_ATTRIB != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES + } + if mask&(FS_MOVE|FS_CREATE|FS_DELETE) != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME + } + return m +} + +func toFSnotifyFlags(action uint32) uint64 { + switch action { + case syscall.FILE_ACTION_ADDED: + return FS_CREATE + case syscall.FILE_ACTION_REMOVED: + return FS_DELETE + case syscall.FILE_ACTION_MODIFIED: + return FS_MODIFY + case syscall.FILE_ACTION_RENAMED_OLD_NAME: + return FS_MOVED_FROM + case syscall.FILE_ACTION_RENAMED_NEW_NAME: + return FS_MOVED_TO + } + return 0 +} + +const ( + // Options for AddWatch + FS_ONESHOT = 0x80000000 + FS_ONLYDIR = 0x1000000 + + // Events + FS_ACCESS = 0x1 + FS_ALL_EVENTS = 0xfff + FS_ATTRIB = 0x4 + FS_CLOSE = 0x18 + FS_CREATE = 0x100 + FS_DELETE = 0x200 + FS_DELETE_SELF = 0x400 + FS_MODIFY = 0x2 + FS_MOVE = 0xc0 + FS_MOVED_FROM = 0x40 + FS_MOVED_TO = 0x80 + FS_MOVE_SELF = 0x800 + + // Special events + FS_IGNORED = 0x8000 + FS_Q_OVERFLOW = 0x4000 +) + +var eventBits = []struct { + Value uint32 + Name string +}{ + {FS_ACCESS, "FS_ACCESS"}, + {FS_ATTRIB, "FS_ATTRIB"}, + {FS_CREATE, "FS_CREATE"}, + {FS_DELETE, "FS_DELETE"}, + {FS_DELETE_SELF, "FS_DELETE_SELF"}, + {FS_MODIFY, "FS_MODIFY"}, + {FS_MOVED_FROM, "FS_MOVED_FROM"}, + {FS_MOVED_TO, "FS_MOVED_TO"}, + {FS_MOVE_SELF, "FS_MOVE_SELF"}, + {FS_IGNORED, "FS_IGNORED"}, + {FS_Q_OVERFLOW, "FS_Q_OVERFLOW"}, +} diff --git a/libgo/go/exp/winfsnotify/winfsnotify_test.go b/libgo/go/exp/winfsnotify/winfsnotify_test.go new file mode 100644 index 00000000000..edf2165c0ec --- /dev/null +++ b/libgo/go/exp/winfsnotify/winfsnotify_test.go @@ -0,0 +1,124 @@ +// 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 winfsnotify + +import ( + "os" + "time" + "testing" +) + +func expect(t *testing.T, eventstream <-chan *Event, name string, mask uint32) { + t.Logf(`expected: "%s": 0x%x`, name, mask) + select { + case event := <-eventstream: + if event == nil { + t.Fatal("nil event received") + } + t.Logf("received: %s", event) + if event.Name != name || event.Mask != mask { + t.Fatal("did not receive expected event") + } + case <-time.After(1e9): + t.Fatal("timed out waiting for event") + } +} + +func TestNotifyEvents(t *testing.T) { + watcher, err := NewWatcher() + if err != nil { + t.Fatalf("NewWatcher() failed: %s", err) + } + + testDir := "TestNotifyEvents.testdirectory" + testFile := testDir + "/TestNotifyEvents.testfile" + testFile2 := testFile + ".new" + const mask = FS_ALL_EVENTS & ^(FS_ATTRIB|FS_CLOSE) | FS_IGNORED + + // Add a watch for testDir + os.RemoveAll(testDir) + if err = os.Mkdir(testDir, 0777); err != nil { + t.Fatalf("Failed to create test directory", err) + } + defer os.RemoveAll(testDir) + err = watcher.AddWatch(testDir, mask) + if err != nil { + t.Fatalf("Watcher.Watch() failed: %s", err) + } + + // Receive errors on the error channel on a separate goroutine + go func() { + for err := range watcher.Error { + t.Fatalf("error received: %s", err) + } + }() + + // Create a file + file, err := os.Create(testFile) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + expect(t, watcher.Event, testFile, FS_CREATE) + + err = watcher.AddWatch(testFile, mask) + if err != nil { + t.Fatalf("Watcher.Watch() failed: %s", err) + } + + if _, err = file.WriteString("hello, world"); err != nil { + t.Fatalf("failed to write to test file: %s", err) + } + if err = file.Sync(); err != nil { + t.Fatalf("failed to sync test file: %s", err) + } + expect(t, watcher.Event, testFile, FS_MODIFY) + expect(t, watcher.Event, testFile, FS_MODIFY) + + if err = file.Close(); err != nil { + t.Fatalf("failed to close test file: %s", err) + } + + if err = os.Rename(testFile, testFile2); err != nil { + t.Fatalf("failed to rename test file: %s", err) + } + expect(t, watcher.Event, testFile, FS_MOVED_FROM) + expect(t, watcher.Event, testFile2, FS_MOVED_TO) + expect(t, watcher.Event, testFile, FS_MOVE_SELF) + + if err = os.RemoveAll(testDir); err != nil { + t.Fatalf("failed to remove test directory: %s", err) + } + expect(t, watcher.Event, testFile2, FS_DELETE_SELF) + expect(t, watcher.Event, testFile2, FS_IGNORED) + expect(t, watcher.Event, testFile2, FS_DELETE) + expect(t, watcher.Event, testDir, FS_DELETE_SELF) + expect(t, watcher.Event, testDir, FS_IGNORED) + + t.Log("calling Close()") + if err = watcher.Close(); err != nil { + t.Fatalf("failed to close watcher: %s", err) + } +} + +func TestNotifyClose(t *testing.T) { + watcher, _ := NewWatcher() + watcher.Close() + + done := false + go func() { + watcher.Close() + done = true + }() + + time.Sleep(50e6) // 50 ms + if !done { + t.Fatal("double Close() test failed: second Close() call didn't return") + } + + err := watcher.Watch("_test") + if err == nil { + t.Fatal("expected error on Watch() after Close(), got nil") + } +} diff --git a/libgo/go/exp/wingui/gui.go b/libgo/go/exp/wingui/gui.go index cf392934c5b..a2f16f282b1 100644 --- a/libgo/go/exp/wingui/gui.go +++ b/libgo/go/exp/wingui/gui.go @@ -25,13 +25,12 @@ func abortErrNo(funcname string, err int) { // global vars var ( - mh uint32 - bh uint32 + mh syscall.Handle + bh syscall.Handle ) // WinProc called by windows to notify us of all windows events we might be interested in. -func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr { - var rc int32 +func WndProc(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) { switch msg { case WM_CREATE: var e int @@ -49,7 +48,7 @@ func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr { fmt.Printf("button handle is %x\n", bh) rc = DefWindowProc(hwnd, msg, wparam, lparam) case WM_COMMAND: - switch uint32(lparam) { + switch syscall.Handle(lparam) { case bh: e := PostMessage(hwnd, WM_CLOSE, 0, 0) if e != 0 { @@ -66,7 +65,7 @@ func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr { rc = DefWindowProc(hwnd, msg, wparam, lparam) } //fmt.Printf("WndProc(0x%08x, %d, 0x%08x, 0x%08x) (%d)\n", hwnd, msg, wparam, lparam, rc) - return uintptr(rc) + return } func rungui() int { diff --git a/libgo/go/exp/wingui/winapi.go b/libgo/go/exp/wingui/winapi.go index 31b57a2cc86..32015287c92 100644 --- a/libgo/go/exp/wingui/winapi.go +++ b/libgo/go/exp/wingui/winapi.go @@ -6,6 +6,7 @@ package main import ( "unsafe" + "syscall" ) type Wndclassex struct { @@ -14,25 +15,25 @@ type Wndclassex struct { WndProc uintptr ClsExtra int32 WndExtra int32 - Instance uint32 - Icon uint32 - Cursor uint32 - Background uint32 + Instance syscall.Handle + Icon syscall.Handle + Cursor syscall.Handle + Background syscall.Handle MenuName *uint16 ClassName *uint16 - IconSm uint32 + IconSm syscall.Handle } type Point struct { - X int32 - Y int32 + X uintptr + Y uintptr } type Msg struct { - Hwnd uint32 + Hwnd syscall.Handle Message uint32 - Wparam int32 - Lparam int32 + Wparam uintptr + Lparam uintptr Time uint32 Pt Point } @@ -109,22 +110,22 @@ var ( IDI_INFORMATION = IDI_ASTERISK ) -//sys GetModuleHandle(modname *uint16) (handle uint32, errno int) = GetModuleHandleW +//sys GetModuleHandle(modname *uint16) (handle syscall.Handle, errno int) = GetModuleHandleW //sys RegisterClassEx(wndclass *Wndclassex) (atom uint16, errno int) = user32.RegisterClassExW -//sys CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent uint32, menu uint32, instance uint32, param uintptr) (hwnd uint32, errno int) = user32.CreateWindowExW -//sys DefWindowProc(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) = user32.DefWindowProcW -//sys DestroyWindow(hwnd uint32) (errno int) = user32.DestroyWindow +//sys CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent syscall.Handle, menu syscall.Handle, instance syscall.Handle, param uintptr) (hwnd syscall.Handle, errno int) = user32.CreateWindowExW +//sys DefWindowProc(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) = user32.DefWindowProcW +//sys DestroyWindow(hwnd syscall.Handle) (errno int) = user32.DestroyWindow //sys PostQuitMessage(exitcode int32) = user32.PostQuitMessage -//sys ShowWindow(hwnd uint32, cmdshow int32) (wasvisible bool) = user32.ShowWindow -//sys UpdateWindow(hwnd uint32) (errno int) = user32.UpdateWindow -//sys GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) [failretval==-1] = user32.GetMessageW +//sys ShowWindow(hwnd syscall.Handle, cmdshow int32) (wasvisible bool) = user32.ShowWindow +//sys UpdateWindow(hwnd syscall.Handle) (errno int) = user32.UpdateWindow +//sys GetMessage(msg *Msg, hwnd syscall.Handle, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) [failretval==-1] = user32.GetMessageW //sys TranslateMessage(msg *Msg) (done bool) = user32.TranslateMessage //sys DispatchMessage(msg *Msg) (ret int32) = user32.DispatchMessageW -//sys LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) = user32.LoadIconW -//sys LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int) = user32.LoadCursorW -//sys SetCursor(cursor uint32) (precursor uint32, errno int) = user32.SetCursor -//sys SendMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) = user32.SendMessageW -//sys PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (errno int) = user32.PostMessageW +//sys LoadIcon(instance syscall.Handle, iconname *uint16) (icon syscall.Handle, errno int) = user32.LoadIconW +//sys LoadCursor(instance syscall.Handle, cursorname *uint16) (cursor syscall.Handle, errno int) = user32.LoadCursorW +//sys SetCursor(cursor syscall.Handle) (precursor syscall.Handle, errno int) = user32.SetCursor +//sys SendMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) = user32.SendMessageW +//sys PostMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (errno int) = user32.PostMessageW func MakeIntResource(id uint16) *uint16 { return (*uint16)(unsafe.Pointer(uintptr(id))) diff --git a/libgo/go/exp/wingui/zwinapi.go b/libgo/go/exp/wingui/zwinapi.go index 4c009dd69bc..38e93eea717 100644 --- a/libgo/go/exp/wingui/zwinapi.go +++ b/libgo/go/exp/wingui/zwinapi.go @@ -28,9 +28,9 @@ var ( procPostMessageW = moduser32.NewProc("PostMessageW") ) -func GetModuleHandle(modname *uint16) (handle uint32, errno int) { +func GetModuleHandle(modname *uint16) (handle syscall.Handle, errno int) { r0, _, e1 := syscall.Syscall(procGetModuleHandleW.Addr(), 1, uintptr(unsafe.Pointer(modname)), 0, 0) - handle = uint32(r0) + handle = syscall.Handle(r0) if handle == 0 { if e1 != 0 { errno = int(e1) @@ -58,9 +58,9 @@ func RegisterClassEx(wndclass *Wndclassex) (atom uint16, errno int) { return } -func CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent uint32, menu uint32, instance uint32, param uintptr) (hwnd uint32, errno int) { +func CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent syscall.Handle, menu syscall.Handle, instance syscall.Handle, param uintptr) (hwnd syscall.Handle, errno int) { r0, _, e1 := syscall.Syscall12(procCreateWindowExW.Addr(), 12, uintptr(exstyle), uintptr(unsafe.Pointer(classname)), uintptr(unsafe.Pointer(windowname)), uintptr(style), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(wndparent), uintptr(menu), uintptr(instance), uintptr(param)) - hwnd = uint32(r0) + hwnd = syscall.Handle(r0) if hwnd == 0 { if e1 != 0 { errno = int(e1) @@ -73,13 +73,13 @@ func CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style return } -func DefWindowProc(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) { +func DefWindowProc(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) { r0, _, _ := syscall.Syscall6(procDefWindowProcW.Addr(), 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) - lresult = int32(r0) + lresult = uintptr(r0) return } -func DestroyWindow(hwnd uint32) (errno int) { +func DestroyWindow(hwnd syscall.Handle) (errno int) { r1, _, e1 := syscall.Syscall(procDestroyWindow.Addr(), 1, uintptr(hwnd), 0, 0) if int(r1) == 0 { if e1 != 0 { @@ -98,13 +98,13 @@ func PostQuitMessage(exitcode int32) { return } -func ShowWindow(hwnd uint32, cmdshow int32) (wasvisible bool) { +func ShowWindow(hwnd syscall.Handle, cmdshow int32) (wasvisible bool) { r0, _, _ := syscall.Syscall(procShowWindow.Addr(), 2, uintptr(hwnd), uintptr(cmdshow), 0) wasvisible = bool(r0 != 0) return } -func UpdateWindow(hwnd uint32) (errno int) { +func UpdateWindow(hwnd syscall.Handle) (errno int) { r1, _, e1 := syscall.Syscall(procUpdateWindow.Addr(), 1, uintptr(hwnd), 0, 0) if int(r1) == 0 { if e1 != 0 { @@ -118,7 +118,7 @@ func UpdateWindow(hwnd uint32) (errno int) { return } -func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) { +func GetMessage(msg *Msg, hwnd syscall.Handle, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) { r0, _, e1 := syscall.Syscall6(procGetMessageW.Addr(), 4, uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax), 0, 0) ret = int32(r0) if ret == -1 { @@ -145,9 +145,9 @@ func DispatchMessage(msg *Msg) (ret int32) { return } -func LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) { +func LoadIcon(instance syscall.Handle, iconname *uint16) (icon syscall.Handle, errno int) { r0, _, e1 := syscall.Syscall(procLoadIconW.Addr(), 2, uintptr(instance), uintptr(unsafe.Pointer(iconname)), 0) - icon = uint32(r0) + icon = syscall.Handle(r0) if icon == 0 { if e1 != 0 { errno = int(e1) @@ -160,9 +160,9 @@ func LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) { return } -func LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int) { +func LoadCursor(instance syscall.Handle, cursorname *uint16) (cursor syscall.Handle, errno int) { r0, _, e1 := syscall.Syscall(procLoadCursorW.Addr(), 2, uintptr(instance), uintptr(unsafe.Pointer(cursorname)), 0) - cursor = uint32(r0) + cursor = syscall.Handle(r0) if cursor == 0 { if e1 != 0 { errno = int(e1) @@ -175,9 +175,9 @@ func LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int) return } -func SetCursor(cursor uint32) (precursor uint32, errno int) { +func SetCursor(cursor syscall.Handle) (precursor syscall.Handle, errno int) { r0, _, e1 := syscall.Syscall(procSetCursor.Addr(), 1, uintptr(cursor), 0, 0) - precursor = uint32(r0) + precursor = syscall.Handle(r0) if precursor == 0 { if e1 != 0 { errno = int(e1) @@ -190,13 +190,13 @@ func SetCursor(cursor uint32) (precursor uint32, errno int) { return } -func SendMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) { +func SendMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) { r0, _, _ := syscall.Syscall6(procSendMessageW.Addr(), 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) - lresult = int32(r0) + lresult = uintptr(r0) return } -func PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (errno int) { +func PostMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (errno int) { r1, _, e1 := syscall.Syscall6(procPostMessageW.Addr(), 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) if int(r1) == 0 { if e1 != 0 { |