From 0b48c8ae1c27bfcc1f5b3f611e64f47321cd18c6 Mon Sep 17 00:00:00 2001 From: Ondřej Surý Date: Wed, 24 Aug 2011 13:23:15 +0200 Subject: Imported Upstream version 2011.08.17 --- src/Make.inc | 26 +- src/cmd/5c/reg.c | 1 + src/cmd/5l/l.h | 1 + src/cmd/6c/cgen.c | 8 +- src/cmd/6l/asm.c | 5 +- src/cmd/6l/l.h | 1 + src/cmd/8l/l.h | 5 +- src/cmd/cgo/out.go | 14 +- src/cmd/gc/Makefile | 4 + src/cmd/gc/builtin.c.boot | 27 +- src/cmd/gc/mkbuiltin | 2 +- src/cmd/gc/mkbuiltin1.c | 6 +- src/cmd/gc/range.c | 2 +- src/cmd/gc/runtime.go | 27 +- src/cmd/gc/select.c | 15 +- src/cmd/gc/walk.c | 18 +- src/cmd/godoc/godoc.go | 316 +- src/cmd/godoc/main.go | 9 +- src/cmd/godoc/snippet.go | 9 +- src/cmd/gofix/Makefile | 1 + src/cmd/gofix/fix.go | 15 +- src/cmd/gofix/url.go | 106 + src/cmd/gofix/url_test.go | 147 + src/cmd/gofmt/test.sh | 2 +- src/cmd/goinstall/main.go | 14 +- src/cmd/goinstall/make.go | 2 +- src/cmd/ld/data.c | 2 +- src/pkg/Makefile | 6 +- src/pkg/asn1/asn1.go | 34 +- src/pkg/asn1/asn1_test.go | 31 + src/pkg/builtin/builtin.go | 135 + src/pkg/crypto/rand/Makefile | 3 + src/pkg/exec/Makefile | 3 + src/pkg/exp/norm/Makefile | 20 +- src/pkg/exp/norm/composition.go | 344 ++ src/pkg/exp/norm/composition_test.go | 138 + src/pkg/exp/norm/forminfo.go | 188 + src/pkg/exp/norm/maketables.go | 190 +- src/pkg/exp/norm/maketesttables.go | 42 + src/pkg/exp/norm/normalize.go | 48 +- src/pkg/exp/norm/tables.go | 7220 +++++++++++++----------------- src/pkg/exp/norm/trie.go | 234 + src/pkg/exp/norm/trie_test.go | 107 + src/pkg/exp/norm/triedata_test.go | 63 + src/pkg/exp/norm/triegen.go | 211 + src/pkg/exp/template/Makefile | 15 - src/pkg/exp/template/doc.go | 300 -- src/pkg/exp/template/exec.go | 633 --- src/pkg/exp/template/exec_test.go | 533 --- src/pkg/exp/template/funcs.go | 352 -- src/pkg/exp/template/helper.go | 238 - src/pkg/exp/template/html/Makefile | 11 + src/pkg/exp/template/html/context.go | 98 + src/pkg/exp/template/html/escape.go | 105 + src/pkg/exp/template/html/escape_test.go | 75 + src/pkg/exp/template/parse.go | 85 - src/pkg/exp/template/parse/Makefile | 14 - src/pkg/exp/template/parse/lex.go | 474 -- src/pkg/exp/template/parse/lex_test.go | 209 - src/pkg/exp/template/parse/node.go | 468 -- src/pkg/exp/template/parse/parse.go | 436 -- src/pkg/exp/template/parse/parse_test.go | 259 -- src/pkg/exp/template/parse/set.go | 50 - src/pkg/exp/template/set.go | 108 - src/pkg/exp/template/set_test.go | 239 - src/pkg/exp/template/testdata/file1.tmpl | 2 - src/pkg/exp/template/testdata/file2.tmpl | 2 - src/pkg/exp/template/testdata/tmpl1.tmpl | 1 - src/pkg/exp/template/testdata/tmpl2.tmpl | 1 - src/pkg/go/doc/comment.go | 2 +- src/pkg/go/parser/parser.go | 127 +- src/pkg/go/parser/parser_test.go | 19 + src/pkg/html/token.go | 2 +- src/pkg/html/token_test.go | 12 +- src/pkg/http/Makefile | 1 - src/pkg/http/cgi/child.go | 5 +- src/pkg/http/cgi/host.go | 2 +- src/pkg/http/client.go | 17 +- src/pkg/http/client_test.go | 13 +- src/pkg/http/fs_test.go | 3 +- src/pkg/http/readrequest_test.go | 13 +- src/pkg/http/request.go | 123 +- src/pkg/http/request_test.go | 19 +- src/pkg/http/requestwrite_test.go | 9 +- src/pkg/http/reverseproxy.go | 3 +- src/pkg/http/reverseproxy_test.go | 3 +- src/pkg/http/serve_test.go | 3 +- src/pkg/http/server.go | 27 +- src/pkg/http/transport.go | 28 +- src/pkg/http/transport_test.go | 69 +- src/pkg/http/url.go | 606 --- src/pkg/http/url_test.go | 698 --- src/pkg/json/decode.go | 6 + src/pkg/json/decode_test.go | 11 +- src/pkg/net/Makefile | 81 +- src/pkg/net/fd.go | 14 +- src/pkg/net/file_plan9.go | 33 + src/pkg/net/file_test.go | 4 +- src/pkg/net/interface_bsd.go | 31 - src/pkg/net/interface_darwin.go | 31 + src/pkg/net/interface_freebsd.go | 31 + src/pkg/net/interface_openbsd.go | 16 + src/pkg/net/iprawsock.go | 294 -- src/pkg/net/iprawsock_plan9.go | 99 + src/pkg/net/iprawsock_posix.go | 305 ++ src/pkg/net/ipsock.go | 165 - src/pkg/net/ipsock_plan9.go | 305 ++ src/pkg/net/ipsock_posix.go | 174 + src/pkg/net/lookup_plan9.go | 226 + src/pkg/net/parse_test.go | 4 +- src/pkg/net/server_test.go | 8 +- src/pkg/net/tcpsock.go | 264 -- src/pkg/net/tcpsock_plan9.go | 63 + src/pkg/net/tcpsock_posix.go | 283 ++ src/pkg/net/udpsock.go | 283 -- src/pkg/net/udpsock_plan9.go | 187 + src/pkg/net/udpsock_posix.go | 294 ++ src/pkg/net/unixsock.go | 399 -- src/pkg/net/unixsock_plan9.go | 105 + src/pkg/net/unixsock_posix.go | 418 ++ src/pkg/old/template/Makefile | 14 + src/pkg/old/template/doc.go | 91 + src/pkg/old/template/execute.go | 346 ++ src/pkg/old/template/format.go | 77 + src/pkg/old/template/parse.go | 743 +++ src/pkg/old/template/template_test.go | 804 ++++ src/pkg/os/user/Makefile | 2 +- src/pkg/os/user/lookup_unix.go | 4 + src/pkg/os/user/user.go | 2 + src/pkg/os/user/user_test.go | 4 +- src/pkg/path/filepath/Makefile | 3 + src/pkg/reflect/value.go | 23 +- src/pkg/rpc/debug.go | 6 +- src/pkg/rpc/server.go | 98 +- src/pkg/rpc/server_test.go | 87 +- src/pkg/runtime/cgo/Makefile | 21 +- src/pkg/runtime/chan.c | 129 +- src/pkg/runtime/chan_test.go | 33 + src/pkg/runtime/hashmap.c | 106 +- src/pkg/runtime/mgc0.c | 12 +- src/pkg/runtime/proc.c | 5 + src/pkg/runtime/runtime.h | 18 +- src/pkg/runtime/type.h | 2 - src/pkg/runtime/windows/386/sys.s | 26 +- src/pkg/runtime/windows/thread.c | 24 +- src/pkg/syscall/route_bsd.go | 29 - src/pkg/syscall/route_darwin.go | 29 + src/pkg/syscall/route_freebsd.go | 29 + src/pkg/syscall/syscall_plan9.go | 7 +- src/pkg/syscall/syscall_windows.go | 12 +- src/pkg/template/Makefile | 9 +- src/pkg/template/doc.go | 392 +- src/pkg/template/exec.go | 655 +++ src/pkg/template/exec_test.go | 580 +++ src/pkg/template/execute.go | 346 -- src/pkg/template/format.go | 77 - src/pkg/template/funcs.go | 368 ++ src/pkg/template/helper.go | 238 + src/pkg/template/parse.go | 762 +--- src/pkg/template/parse/Makefile | 14 + src/pkg/template/parse/lex.go | 474 ++ src/pkg/template/parse/lex_test.go | 209 + src/pkg/template/parse/node.go | 469 ++ src/pkg/template/parse/parse.go | 436 ++ src/pkg/template/parse/parse_test.go | 259 ++ src/pkg/template/parse/set.go | 50 + src/pkg/template/set.go | 108 + src/pkg/template/set_test.go | 239 + src/pkg/template/template_test.go | 804 ---- src/pkg/template/testdata/file1.tmpl | 2 + src/pkg/template/testdata/file2.tmpl | 2 + src/pkg/template/testdata/tmpl1.tmpl | 1 + src/pkg/template/testdata/tmpl2.tmpl | 1 + src/pkg/time/Makefile | 5 + src/pkg/time/format.go | 83 +- src/pkg/time/time_test.go | 7 + src/pkg/url/Makefile | 11 + src/pkg/url/url.go | 677 +++ src/pkg/url/url_test.go | 698 +++ src/pkg/utf8/utf8.go | 120 +- src/pkg/websocket/client.go | 9 +- src/pkg/websocket/websocket_test.go | 5 +- src/pkg/xml/marshal.go | 2 +- src/pkg/xml/marshal_test.go | 6 + src/run.bash | 6 +- src/version.bash | 22 +- 186 files changed, 16990 insertions(+), 14390 deletions(-) create mode 100644 src/cmd/gofix/url.go create mode 100644 src/cmd/gofix/url_test.go create mode 100644 src/pkg/builtin/builtin.go create mode 100644 src/pkg/exp/norm/composition.go create mode 100644 src/pkg/exp/norm/composition_test.go create mode 100644 src/pkg/exp/norm/forminfo.go create mode 100644 src/pkg/exp/norm/maketesttables.go create mode 100644 src/pkg/exp/norm/trie.go create mode 100644 src/pkg/exp/norm/trie_test.go create mode 100644 src/pkg/exp/norm/triedata_test.go create mode 100644 src/pkg/exp/norm/triegen.go delete mode 100644 src/pkg/exp/template/Makefile delete mode 100644 src/pkg/exp/template/doc.go delete mode 100644 src/pkg/exp/template/exec.go delete mode 100644 src/pkg/exp/template/exec_test.go delete mode 100644 src/pkg/exp/template/funcs.go delete mode 100644 src/pkg/exp/template/helper.go create mode 100644 src/pkg/exp/template/html/Makefile create mode 100644 src/pkg/exp/template/html/context.go create mode 100644 src/pkg/exp/template/html/escape.go create mode 100644 src/pkg/exp/template/html/escape_test.go delete mode 100644 src/pkg/exp/template/parse.go delete mode 100644 src/pkg/exp/template/parse/Makefile delete mode 100644 src/pkg/exp/template/parse/lex.go delete mode 100644 src/pkg/exp/template/parse/lex_test.go delete mode 100644 src/pkg/exp/template/parse/node.go delete mode 100644 src/pkg/exp/template/parse/parse.go delete mode 100644 src/pkg/exp/template/parse/parse_test.go delete mode 100644 src/pkg/exp/template/parse/set.go delete mode 100644 src/pkg/exp/template/set.go delete mode 100644 src/pkg/exp/template/set_test.go delete mode 100644 src/pkg/exp/template/testdata/file1.tmpl delete mode 100644 src/pkg/exp/template/testdata/file2.tmpl delete mode 100644 src/pkg/exp/template/testdata/tmpl1.tmpl delete mode 100644 src/pkg/exp/template/testdata/tmpl2.tmpl delete mode 100644 src/pkg/http/url.go delete mode 100644 src/pkg/http/url_test.go create mode 100644 src/pkg/net/file_plan9.go create mode 100644 src/pkg/net/interface_openbsd.go create mode 100644 src/pkg/net/iprawsock_plan9.go create mode 100644 src/pkg/net/iprawsock_posix.go create mode 100644 src/pkg/net/ipsock_plan9.go create mode 100644 src/pkg/net/ipsock_posix.go create mode 100644 src/pkg/net/lookup_plan9.go create mode 100644 src/pkg/net/tcpsock_plan9.go create mode 100644 src/pkg/net/tcpsock_posix.go create mode 100644 src/pkg/net/udpsock_plan9.go create mode 100644 src/pkg/net/udpsock_posix.go create mode 100644 src/pkg/net/unixsock_plan9.go create mode 100644 src/pkg/net/unixsock_posix.go create mode 100644 src/pkg/old/template/Makefile create mode 100644 src/pkg/old/template/doc.go create mode 100644 src/pkg/old/template/execute.go create mode 100644 src/pkg/old/template/format.go create mode 100644 src/pkg/old/template/parse.go create mode 100644 src/pkg/old/template/template_test.go create mode 100644 src/pkg/template/exec.go create mode 100644 src/pkg/template/exec_test.go delete mode 100644 src/pkg/template/execute.go delete mode 100644 src/pkg/template/format.go create mode 100644 src/pkg/template/funcs.go create mode 100644 src/pkg/template/helper.go create mode 100644 src/pkg/template/parse/Makefile create mode 100644 src/pkg/template/parse/lex.go create mode 100644 src/pkg/template/parse/lex_test.go create mode 100644 src/pkg/template/parse/node.go create mode 100644 src/pkg/template/parse/parse.go create mode 100644 src/pkg/template/parse/parse_test.go create mode 100644 src/pkg/template/parse/set.go create mode 100644 src/pkg/template/set.go create mode 100644 src/pkg/template/set_test.go delete mode 100644 src/pkg/template/template_test.go create mode 100644 src/pkg/template/testdata/file1.tmpl create mode 100644 src/pkg/template/testdata/file2.tmpl create mode 100644 src/pkg/template/testdata/tmpl1.tmpl create mode 100644 src/pkg/template/testdata/tmpl2.tmpl create mode 100644 src/pkg/url/Makefile create mode 100644 src/pkg/url/url.go create mode 100644 src/pkg/url/url_test.go (limited to 'src') diff --git a/src/Make.inc b/src/Make.inc index a6edb165a..8f549f624 100644 --- a/src/Make.inc +++ b/src/Make.inc @@ -121,6 +121,21 @@ HOST_CFLAGS=-I"$(GOROOT)/include" $(HOST_EXTRA_CFLAGS) HOST_LDFLAGS=$(HOST_EXTRA_LDFLAGS) PWD=$(shell pwd) +# Decide whether use of cgo is okay. +ifeq ($(CGO_ENABLED),) +# Default on... +CGO_ENABLED:=1 +ifeq ($(GOARCH),arm) # ... but not on ARM +CGO_ENABLED:=0 +endif +ifeq ($(GOOS),plan9) # ... and not on Plan 9 +CGO_ENABLED:=0 +endif +ifeq ($(GOOS),openbsd) # ... and not on OpenBSD +CGO_ENABLED:=0 +endif +endif + # Make environment more standard. LANG:= LC_ALL:=C @@ -130,11 +145,12 @@ GREP_COLORS:= export LANG LC_ALL LC_CTYPE GREP_OPTIONS GREP_COLORS go-env: - @echo export GOARCH=$(GOARCH) - @echo export GOOS=$(GOOS) - @echo export GOHOSTARCH=$(GOHOSTARCH) - @echo export GOHOSTOS=$(GOHOSTOS) - @echo export O=$O + @echo export GOARCH="$(GOARCH)" + @echo export GOOS="$(GOOS)" + @echo export GOHOSTARCH="$(GOHOSTARCH)" + @echo export GOHOSTOS="$(GOHOSTOS)" + @echo export CGO_ENABLED="$(CGO_ENABLED)" + @echo export O="$O" @echo export AS="$(AS)" @echo export CC="$(CC)" @echo export GC="$(GC)" diff --git a/src/cmd/5c/reg.c b/src/cmd/5c/reg.c index 50b814598..1ccf74a35 100644 --- a/src/cmd/5c/reg.c +++ b/src/cmd/5c/reg.c @@ -66,6 +66,7 @@ rcmp(const void *a1, const void *a2) void regopt(Prog *p) { + USED(p); // TODO(kaib): optimizer disabled because it smashes R8 when running out of registers // the disable is unconventionally here because the call is in common code shared by 5c/6c/8c return; diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index e00f536a7..dabe93d37 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -324,6 +324,7 @@ EXTERN Prog* prog_modu; #pragma varargck type "N" Adr* #pragma varargck type "P" Prog* #pragma varargck type "S" char* +#pragma varargck type "Z" char* #pragma varargck type "i" char* int Aconv(Fmt*); diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c index 7aa4aa976..7f717dcbb 100644 --- a/src/cmd/6c/cgen.c +++ b/src/cmd/6c/cgen.c @@ -930,9 +930,6 @@ cgen(Node *n, Node *nn) return; } - o = 0; - if(REGARG >= 0) - o = reg[REGARG]; gargs(r, &nod, &nod1); if(l->addable < INDEXED) { reglcgen(&nod, l, nn); @@ -941,9 +938,8 @@ cgen(Node *n, Node *nn) regfree(&nod); } else gopcode(OFUNC, n->type, Z, l); - if(REGARG >= 0) - if(o != reg[REGARG]) - reg[REGARG]--; + if(REGARG >= 0 && reg[REGARG]) + reg[REGARG]--; if(nn != Z) { regret(&nod, n); gmove(&nod, nn); diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index f59a59efb..3a8223e65 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -298,6 +298,9 @@ adddynrel(Sym *s, Reloc *r) int archreloc(Reloc *r, Sym *s, vlong *val) { + USED(r); + USED(s); + USED(val); return -1; } @@ -859,7 +862,7 @@ asmb(void) startva = INITTEXT - HEADR; /* This null SHdr must appear before all others */ - sh = newElfShdr(elfstr[ElfStrEmpty]); + newElfShdr(elfstr[ElfStrEmpty]); /* program header info */ pph = newElfPhdr(); diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 043568585..b291d5f3d 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -410,6 +410,7 @@ uint32 machheadr(void); #pragma varargck type "D" Adr* #pragma varargck type "P" Prog* #pragma varargck type "R" int +#pragma varargck type "Z" char* #pragma varargck type "A" int #pragma varargck argpos diag 1 diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index 94cbfc26a..4ee0db967 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -46,10 +46,6 @@ enum #define P ((Prog*)0) #define S ((Sym*)0) #define TNAME (cursym?cursym->name:noname) -#define cput(c)\ - { *cbp++ = c;\ - if(--cbc <= 0)\ - cflush(); } typedef struct Adr Adr; typedef struct Prog Prog; @@ -254,6 +250,7 @@ enum #pragma varargck type "R" int #pragma varargck type "S" char* #pragma varargck type "Y" Sym* +#pragma varargck type "Z" char* #pragma varargck type "i" char* EXTERN int32 HEADR; diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 9c962b8ff..498ab1566 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -59,17 +59,21 @@ func (p *Package) writeDefs() { fmt.Fprintf(fc, cProlog) - var cVars []string + cVars := make(map[string]bool) for _, n := range p.Name { if n.Kind != "var" { continue } - cVars = append(cVars, n.C) - fmt.Fprintf(fm, "extern char %s[];\n", n.C) - fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) + if !cVars[n.C] { + fmt.Fprintf(fm, "extern char %s[];\n", n.C) + fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) + + fmt.Fprintf(fc, "extern byte *%s;\n", n.C) + + cVars[n.C] = true + } - fmt.Fprintf(fc, "extern byte *%s;\n", n.C) fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) fmt.Fprintf(fc, "\n") diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile index 286618ec1..0af7659e4 100644 --- a/src/cmd/gc/Makefile +++ b/src/cmd/gc/Makefile @@ -65,3 +65,7 @@ opnames.h: mkopnames go.h ./mkopnames go.h >opnames.h CLEANFILES+=*.[568] [568].out y1.tab.c yerr.h mkbuiltin1 builtin.c _builtin.c opnames.h + +mkbuiltin1: mkbuiltin1.$O + $(HOST_LD) -o $@ mkbuiltin1.$O -L"$(GOROOT)"/lib -lbio -l9 -lm $(HOST_LDFLAGS) + diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot index 6419873a2..190c56008 100644 --- a/src/cmd/gc/builtin.c.boot +++ b/src/cmd/gc/builtin.c.boot @@ -57,24 +57,23 @@ char *runtimeimport = "func \"\".efaceeq (i1 any, i2 any) bool\n" "func \"\".ifacethash (i1 any) uint32\n" "func \"\".efacethash (i1 any) uint32\n" - "func \"\".makemap (key *uint8, val *uint8, hint int64) map[any] any\n" - "func \"\".mapaccess1 (hmap map[any] any, key any) any\n" - "func \"\".mapaccess2 (hmap map[any] any, key any) (val any, pres bool)\n" - "func \"\".mapassign1 (hmap map[any] any, key any, val any)\n" - "func \"\".mapassign2 (hmap map[any] any, key any, val any, pres bool)\n" - "func \"\".mapiterinit (hmap map[any] any, hiter *any)\n" + "func \"\".makemap (mapType *uint8, hint int64) map[any] any\n" + "func \"\".mapaccess1 (mapType *uint8, hmap map[any] any, key any) any\n" + "func \"\".mapaccess2 (mapType *uint8, hmap map[any] any, key any) (val any, pres bool)\n" + "func \"\".mapassign1 (mapType *uint8, hmap map[any] any, key any, val any)\n" + "func \"\".mapassign2 (mapType *uint8, hmap map[any] any, key any, val any, pres bool)\n" + "func \"\".mapiterinit (mapType *uint8, hmap map[any] any, hiter *any)\n" "func \"\".mapiternext (hiter *any)\n" "func \"\".mapiter1 (hiter *any) any\n" "func \"\".mapiter2 (hiter *any) (key any, val any)\n" - "func \"\".makechan (elem *uint8, hint int64) chan any\n" - "func \"\".chanrecv1 (hchan <-chan any) any\n" - "func \"\".chanrecv2 (hchan <-chan any) (elem any, received bool)\n" - "func \"\".chansend1 (hchan chan<- any, elem any)\n" + "func \"\".makechan (chanType *uint8, hint int64) chan any\n" + "func \"\".chanrecv1 (chanType *uint8, hchan <-chan any) any\n" + "func \"\".chanrecv2 (chanType *uint8, hchan <-chan any) (elem any, received bool)\n" + "func \"\".chansend1 (chanType *uint8, hchan chan<- any, elem any)\n" "func \"\".closechan (hchan any)\n" - "func \"\".closedchan (hchan any) bool\n" - "func \"\".selectnbsend (hchan chan<- any, elem any) bool\n" - "func \"\".selectnbrecv (elem *any, hchan <-chan any) bool\n" - "func \"\".selectnbrecv2 (elem *any, received *bool, hchan <-chan any) bool\n" + "func \"\".selectnbsend (chanType *uint8, hchan chan<- any, elem any) bool\n" + "func \"\".selectnbrecv (chanType *uint8, elem *any, hchan <-chan any) bool\n" + "func \"\".selectnbrecv2 (chanType *uint8, elem *any, received *bool, hchan <-chan any) bool\n" "func \"\".newselect (size int) *uint8\n" "func \"\".selectsend (sel *uint8, hchan chan<- any, elem *any) bool\n" "func \"\".selectrecv (sel *uint8, hchan <-chan any, elem *any) bool\n" diff --git a/src/cmd/gc/mkbuiltin b/src/cmd/gc/mkbuiltin index 4dfff1caa..cfd6e59c1 100755 --- a/src/cmd/gc/mkbuiltin +++ b/src/cmd/gc/mkbuiltin @@ -16,7 +16,7 @@ if [ -z "$GC" ]; then exit 1 fi -gcc -o mkbuiltin1 mkbuiltin1.c +gomake mkbuiltin1 rm -f _builtin.c for i in runtime unsafe do diff --git a/src/cmd/gc/mkbuiltin1.c b/src/cmd/gc/mkbuiltin1.c index baa87fec9..ad83c0346 100644 --- a/src/cmd/gc/mkbuiltin1.c +++ b/src/cmd/gc/mkbuiltin1.c @@ -10,7 +10,7 @@ void esc(char*); -int +void main(int argc, char **argv) { char *name; @@ -64,13 +64,13 @@ begin: } esc(p); - printf("\\n\"\n", p); + printf("\\n\"\n"); } sysfatal("did not find end of imports\n"); end: printf("\t\"$$\\n\";\n"); - return 0; + exits(0); } void diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index fb33e4e48..5ce693ae3 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -175,7 +175,7 @@ walkrange(Node *n) argtype(fn, t->down); argtype(fn, t->type); argtype(fn, th); - init = list(init, mkcall1(fn, T, nil, ha, nod(OADDR, hit, N))); + init = list(init, mkcall1(fn, T, nil, typename(t), ha, nod(OADDR, hit, N))); n->ntest = nod(ONE, nod(OINDEX, hit, nodintconst(0)), nodnil()); fn = syslook("mapiternext", 1); diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 7254f874e..549f7abe3 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -80,27 +80,26 @@ func ifacethash(i1 any) (ret uint32) func efacethash(i1 any) (ret uint32) // *byte is really *runtime.Type -func makemap(key, val *byte, hint int64) (hmap map[any]any) -func mapaccess1(hmap map[any]any, key any) (val any) -func mapaccess2(hmap map[any]any, key any) (val any, pres bool) -func mapassign1(hmap map[any]any, key any, val any) -func mapassign2(hmap map[any]any, key any, val any, pres bool) -func mapiterinit(hmap map[any]any, hiter *any) +func makemap(mapType *byte, hint int64) (hmap map[any]any) +func mapaccess1(mapType *byte, hmap map[any]any, key any) (val any) +func mapaccess2(mapType *byte, hmap map[any]any, key any) (val any, pres bool) +func mapassign1(mapType *byte, hmap map[any]any, key any, val any) +func mapassign2(mapType *byte, hmap map[any]any, key any, val any, pres bool) +func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) func mapiternext(hiter *any) func mapiter1(hiter *any) (key any) func mapiter2(hiter *any) (key any, val any) // *byte is really *runtime.Type -func makechan(elem *byte, hint int64) (hchan chan any) -func chanrecv1(hchan <-chan any) (elem any) -func chanrecv2(hchan <-chan any) (elem any, received bool) -func chansend1(hchan chan<- any, elem any) +func makechan(chanType *byte, hint int64) (hchan chan any) +func chanrecv1(chanType *byte, hchan <-chan any) (elem any) +func chanrecv2(chanType *byte, hchan <-chan any) (elem any, received bool) +func chansend1(chanType *byte, hchan chan<- any, elem any) func closechan(hchan any) -func closedchan(hchan any) bool -func selectnbsend(hchan chan<- any, elem any) bool -func selectnbrecv(elem *any, hchan <-chan any) bool -func selectnbrecv2(elem *any, received *bool, hchan <-chan any) bool +func selectnbsend(chanType *byte, hchan chan<- any, elem any) bool +func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool +func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool func newselect(size int) (sel *byte) func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool) diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index 8395dda31..909ad3aa4 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -250,9 +250,8 @@ walkselect(Node *sel) case OSEND: // if c != nil && selectnbsend(c, v) { body } else { default body } ch = cheapexpr(n->left, &r->ninit); - r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()), - mkcall1(chanfn("selectnbsend", 2, ch->type), - types[TBOOL], &r->ninit, ch, n->right)); + r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type), + types[TBOOL], &r->ninit, typename(ch->type), ch, n->right); break; case OSELRECV: @@ -260,9 +259,8 @@ walkselect(Node *sel) r = nod(OIF, N, N); r->ninit = cas->ninit; ch = cheapexpr(n->right->left, &r->ninit); - r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()), - mkcall1(chanfn("selectnbrecv", 2, ch->type), - types[TBOOL], &r->ninit, n->left, ch)); + r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type), + types[TBOOL], &r->ninit, typename(ch->type), n->left, ch); break; case OSELRECV2: @@ -270,9 +268,8 @@ walkselect(Node *sel) r = nod(OIF, N, N); r->ninit = cas->ninit; ch = cheapexpr(n->right->left, &r->ninit); - r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()), - mkcall1(chanfn("selectnbrecv2", 2, ch->type), - types[TBOOL], &r->ninit, n->left, n->ntest, ch)); + r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type), + types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch); break; } typecheck(&r->ntest, Erv); diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 0383e5a6a..9cd4ee919 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -591,7 +591,7 @@ walkexpr(Node **np, NodeList **init) walkexprlistsafe(n->list, init); walkexpr(&r->left, init); fn = chanfn("chanrecv2", 2, r->left->type); - r = mkcall1(fn, getoutargx(fn->type), init, r->left); + r = mkcall1(fn, getoutargx(fn->type), init, typename(r->left->type), r->left); n->rlist->n = r; n->op = OAS2FUNC; goto as2func; @@ -604,7 +604,7 @@ walkexpr(Node **np, NodeList **init) walkexprlistsafe(n->list, init); walkexpr(&r->left, init); fn = mapfn("mapaccess2", r->left->type); - r = mkcall1(fn, getoutargx(fn->type), init, r->left, r->right); + r = mkcall1(fn, getoutargx(fn->type), init, typename(r->left->type), r->left, r->right); n->rlist = list1(r); n->op = OAS2FUNC; goto as2func; @@ -617,7 +617,7 @@ walkexpr(Node **np, NodeList **init) walkexprlistsafe(n->list, init); l = n->list->n; t = l->left->type; - n = mkcall1(mapfn("mapassign2", t), T, init, l->left, l->right, n->rlist->n, n->rlist->next->n); + n = mkcall1(mapfn("mapassign2", t), T, init, typename(t), l->left, l->right, n->rlist->n, n->rlist->next->n); goto ret; case OAS2DOTTYPE: @@ -852,13 +852,13 @@ walkexpr(Node **np, NodeList **init) goto ret; t = n->left->type; - n = mkcall1(mapfn("mapaccess1", t), t->type, init, n->left, n->right); + n = mkcall1(mapfn("mapaccess1", t), t->type, init, typename(t), n->left, n->right); goto ret; case ORECV: walkexpr(&n->left, init); walkexpr(&n->right, init); - n = mkcall1(chanfn("chanrecv1", 2, n->left->type), n->type, init, n->left); + n = mkcall1(chanfn("chanrecv1", 2, n->left->type), n->type, init, typename(n->left->type), n->left); goto ret; case OSLICE: @@ -1078,7 +1078,7 @@ walkexpr(Node **np, NodeList **init) case OMAKECHAN: n = mkcall1(chanfn("makechan", 1, n->type), n->type, init, - typename(n->type->type), + typename(n->type), conv(n->left, types[TINT64])); goto ret; @@ -1090,8 +1090,7 @@ walkexpr(Node **np, NodeList **init) argtype(fn, t->type); // any-2 n = mkcall1(fn, n->type, init, - typename(t->down), // key type - typename(t->type), // value type + typename(n->type), conv(n->left, types[TINT64])); goto ret; @@ -1164,7 +1163,7 @@ walkexpr(Node **np, NodeList **init) goto ret; case OSEND: - n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, n->left, n->right); + n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n->right); goto ret; case OCLOSURE: @@ -1697,6 +1696,7 @@ convas(Node *n, NodeList **init) if(n->left->op == OINDEXMAP) { n = mkcall1(mapfn("mapassign1", n->left->left->type), T, init, + typename(n->left->left->type), n->left->left, n->left->right, n->right); goto out; } diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index 03ac1b98b..b8a839404 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -371,143 +371,24 @@ func writeNode(w io.Writer, fset *token.FileSet, x interface{}) { (&printer.Config{mode, *tabwidth}).Fprint(&tconv{output: w}, fset, x) } -// Write anything to w. -func writeAny(w io.Writer, fset *token.FileSet, x interface{}) { - switch v := x.(type) { - case []byte: - w.Write(v) - case string: - w.Write([]byte(v)) - case ast.Decl, ast.Expr, ast.Stmt, *ast.File: - writeNode(w, fset, x) - default: - fmt.Fprint(w, x) - } -} - -// Write anything html-escaped to w. -func writeAnyHTML(w io.Writer, fset *token.FileSet, x interface{}) { - switch v := x.(type) { - case []byte: - template.HTMLEscape(w, v) - case string: - template.HTMLEscape(w, []byte(v)) - case ast.Decl, ast.Expr, ast.Stmt, *ast.File: - var buf bytes.Buffer - writeNode(&buf, fset, x) - FormatText(w, buf.Bytes(), -1, true, "", nil) - default: - var buf bytes.Buffer - fmt.Fprint(&buf, x) - template.HTMLEscape(w, buf.Bytes()) - } +func filenameFunc(path string) string { + _, localname := filepath.Split(path) + return localname } -func fileset(x []interface{}) *token.FileSet { - if len(x) > 1 { - if fset, ok := x[1].(*token.FileSet); ok { - return fset - } +func fileInfoNameFunc(fi FileInfo) string { + name := fi.Name() + if fi.IsDirectory() { + name += "/" } - return nil -} - -// Template formatter for "html-esc" format. -func htmlEscFmt(w io.Writer, format string, x ...interface{}) { - writeAnyHTML(w, fileset(x), x[0]) -} - -// Template formatter for "html-comment" format. -func htmlCommentFmt(w io.Writer, format string, x ...interface{}) { - var buf bytes.Buffer - writeAny(&buf, fileset(x), x[0]) - // TODO(gri) Provide list of words (e.g. function parameters) - // to be emphasized by ToHTML. - doc.ToHTML(w, buf.Bytes(), nil) // does html-escaping -} - -// Template formatter for "" (default) format. -func textFmt(w io.Writer, format string, x ...interface{}) { - writeAny(w, fileset(x), x[0]) + return name } -// Template formatter for "urlquery-esc" format. -func urlQueryEscFmt(w io.Writer, format string, x ...interface{}) { - var buf bytes.Buffer - writeAny(&buf, fileset(x), x[0]) - template.HTMLEscape(w, []byte(http.URLEscape(string(buf.Bytes())))) -} - -// Template formatter for the various "url-xxx" formats excluding url-esc. -func urlFmt(w io.Writer, format string, x ...interface{}) { - var path string - var line int - var low, high int // selection - - // determine path and position info, if any - type positioner interface { - Pos() token.Pos - End() token.Pos - } - switch t := x[0].(type) { - case string: - path = t - case positioner: - fset := fileset(x) - if p := t.Pos(); p.IsValid() { - pos := fset.Position(p) - path = pos.Filename - line = pos.Line - low = pos.Offset - } - if p := t.End(); p.IsValid() { - high = fset.Position(p).Offset - } - default: - // we should never reach here, but be resilient - // and assume the position is invalid (empty path, - // and line 0) - log.Printf("INTERNAL ERROR: urlFmt(%s) without a string or positioner", format) - } - - // map path - relpath := relativeURL(path) - - // convert to relative URLs so that they can also - // be used as relative file names in .txt templates - switch format { - default: - // we should never reach here, but be resilient - // and assume the url-pkg format instead - log.Printf("INTERNAL ERROR: urlFmt(%s)", format) - fallthrough - case "url-pkg": - // because of the irregular mapping under goroot - // we need to correct certain relative paths - if strings.HasPrefix(relpath, "src/pkg/") { - relpath = relpath[len("src/pkg/"):] - } - template.HTMLEscape(w, []byte(pkgHandler.pattern[1:]+relpath)) // remove trailing '/' for relative URL - case "url-src": - template.HTMLEscape(w, []byte(relpath)) - case "url-pos": - template.HTMLEscape(w, []byte(relpath)) - // selection ranges are of form "s=low:high" - if low < high { - fmt.Fprintf(w, "?s=%d:%d", low, high) - // if we have a selection, position the page - // such that the selection is a bit below the top - line -= 10 - if line < 1 { - line = 1 - } - } - // line id's in html-printed source are of the - // form "L%d" where %d stands for the line number - if line > 0 { - fmt.Fprintf(w, "#L%d", line) - } +func fileInfoTimeFunc(fi FileInfo) string { + if t := fi.Mtime_ns(); t != 0 { + return time.SecondsToLocalTime(t / 1e9).String() } + return "" // don't return epoch if time is obviously not set } // The strings in infoKinds must be properly html-escaped. @@ -522,14 +403,11 @@ var infoKinds = [nKinds]string{ Use: "use", } -// Template formatter for "infoKind" format. -func infoKindFmt(w io.Writer, format string, x ...interface{}) { - fmt.Fprintf(w, infoKinds[x[0].(SpotKind)]) // infoKind entries are html-escaped +func infoKind_htmlFunc(kind SpotKind) string { + return infoKinds[kind] // infoKind entries are html-escaped } -// Template formatter for "infoLine" format. -func infoLineFmt(w io.Writer, format string, x ...interface{}) { - info := x[0].(SpotInfo) +func infoLineFunc(info SpotInfo) int { line := info.Lori() if info.IsIndex() { index, _ := searchIndex.get() @@ -543,80 +421,113 @@ func infoLineFmt(w io.Writer, format string, x ...interface{}) { line = 0 } } - fmt.Fprintf(w, "%d", line) + return line } -// Template formatter for "infoSnippet" format. -func infoSnippetFmt(w io.Writer, format string, x ...interface{}) { - info := x[0].(SpotInfo) - text := []byte(`no snippet text available`) +func infoSnippet_htmlFunc(info SpotInfo) string { if info.IsIndex() { index, _ := searchIndex.get() - // no escaping of snippet text needed; - // snippet text is escaped when generated - text = index.(*Index).Snippet(info.Lori()).Text + // Snippet.Text was HTML-escaped when it was generated + return index.(*Index).Snippet(info.Lori()).Text } - w.Write(text) + return `no snippet text available` } -// Template formatter for "padding" format. -func paddingFmt(w io.Writer, format string, x ...interface{}) { - for i := x[0].(int); i > 0; i-- { - fmt.Fprint(w, ``) - } +func nodeFunc(node interface{}, fset *token.FileSet) string { + var buf bytes.Buffer + writeNode(&buf, fset, node) + return buf.String() } -// Template formatter for "localname" format. -func localnameFmt(w io.Writer, format string, x ...interface{}) { - _, localname := filepath.Split(x[0].(string)) - template.HTMLEscape(w, []byte(localname)) +func node_htmlFunc(node interface{}, fset *token.FileSet) string { + var buf1 bytes.Buffer + writeNode(&buf1, fset, node) + var buf2 bytes.Buffer + FormatText(&buf2, buf1.Bytes(), -1, true, "", nil) + return buf2.String() } -// Template formatter for "fileInfoName" format. -func fileInfoNameFmt(w io.Writer, format string, x ...interface{}) { - fi := x[0].(FileInfo) - template.HTMLEscape(w, []byte(fi.Name())) - if fi.IsDirectory() { - w.Write([]byte{'/'}) - } +func comment_htmlFunc(comment string) string { + var buf bytes.Buffer + // TODO(gri) Provide list of words (e.g. function parameters) + // to be emphasized by ToHTML. + doc.ToHTML(&buf, []byte(comment), nil) // does html-escaping + return buf.String() } -// Template formatter for "fileInfoSize" format. -func fileInfoSizeFmt(w io.Writer, format string, x ...interface{}) { - fmt.Fprintf(w, "%d", x[0].(FileInfo).Size()) +func pkgLinkFunc(path string) string { + relpath := relativeURL(path) + // because of the irregular mapping under goroot + // we need to correct certain relative paths + if strings.HasPrefix(relpath, "src/pkg/") { + relpath = relpath[len("src/pkg/"):] + } + return pkgHandler.pattern[1:] + relpath // remove trailing '/' for relative URL } -// Template formatter for "fileInfoTime" format. -func fileInfoTimeFmt(w io.Writer, format string, x ...interface{}) { - if t := x[0].(FileInfo).Mtime_ns(); t != 0 { - template.HTMLEscape(w, []byte(time.SecondsToLocalTime(t/1e9).String())) +func posLink_urlFunc(node ast.Node, fset *token.FileSet) string { + var relpath string + var line int + var low, high int // selection + + if p := node.Pos(); p.IsValid() { + pos := fset.Position(p) + relpath = relativeURL(pos.Filename) + line = pos.Line + low = pos.Offset + } + if p := node.End(); p.IsValid() { + high = fset.Position(p).Offset + } + + var buf bytes.Buffer + template.HTMLEscape(&buf, []byte(relpath)) + // selection ranges are of form "s=low:high" + if low < high { + fmt.Fprintf(&buf, "?s=%d:%d", low, high) // no need for URL escaping + // if we have a selection, position the page + // such that the selection is a bit below the top + line -= 10 + if line < 1 { + line = 1 + } + } + // line id's in html-printed source are of the + // form "L%d" where %d stands for the line number + if line > 0 { + fmt.Fprintf(&buf, "#L%d", line) // no need for URL escaping } - // don't print epoch if time is obviously not set -} -// Template formatter for "numlines" format. -func numlinesFmt(w io.Writer, format string, x ...interface{}) { - list := x[0].([]int) - fmt.Fprintf(w, "%d", len(list)) + return buf.String() } -var fmap = template.FormatterMap{ - "": textFmt, - "html-esc": htmlEscFmt, - "html-comment": htmlCommentFmt, - "urlquery-esc": urlQueryEscFmt, - "url-pkg": urlFmt, - "url-src": urlFmt, - "url-pos": urlFmt, - "infoKind": infoKindFmt, - "infoLine": infoLineFmt, - "infoSnippet": infoSnippetFmt, - "padding": paddingFmt, - "fileInfoName": fileInfoNameFmt, - "fileInfoSize": fileInfoSizeFmt, - "fileInfoTime": fileInfoTimeFmt, - "localname": localnameFmt, - "numlines": numlinesFmt, +// fmap describes the template functions installed with all godoc templates. +// Convention: template function names ending in "_html" or "_url" produce +// HTML- or URL-escaped strings; all other function results may +// require explicit escaping in the template. +var fmap = template.FuncMap{ + // various helpers + "filename": filenameFunc, + "repeat": strings.Repeat, + + // accss to FileInfos (directory listings) + "fileInfoName": fileInfoNameFunc, + "fileInfoTime": fileInfoTimeFunc, + + // access to search result information + "infoKind_html": infoKind_htmlFunc, + "infoLine": infoLineFunc, + "infoSnippet_html": infoSnippet_htmlFunc, + + // formatting of AST nodes + "node": nodeFunc, + "node_html": node_htmlFunc, + "comment_html": comment_htmlFunc, + + // support for URL attributes + "pkgLink": pkgLinkFunc, + "srcLink": relativeURL, + "posLink_url": posLink_urlFunc, } func readTemplate(name string) *template.Template { @@ -629,15 +540,7 @@ func readTemplate(name string) *template.Template { path = defaultpath } } - data, err := fs.ReadFile(path) - if err != nil { - log.Fatalf("ReadFile %s: %v", path, err) - } - t, err := template.Parse(string(data), fmap) - if err != nil { - log.Fatalf("%s: %v", name, err) - } - return t + return template.Must(template.New(name).Funcs(fmap).ParseFile(path)) } var ( @@ -864,6 +767,10 @@ func serveFile(w http.ResponseWriter, r *http.Request) { const fakePkgFile = "doc.go" const fakePkgName = "documentation" +// Fake relative package path for built-ins. Documentation for all globals +// (not just exported ones) will be shown for packages in this directory. +const builtinPkgPath = "builtin/" + type PageInfoMode uint const ( @@ -1038,7 +945,10 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { relpath := r.URL.Path[len(h.pattern):] abspath := absolutePath(relpath, h.fsRoot) - mode := exportsOnly + var mode PageInfoMode + if relpath != builtinPkgPath { + mode = exportsOnly + } if r.FormValue("m") != "src" { mode |= genDoc } @@ -1061,7 +971,7 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { title = "Package " + info.PAst.Name.Name case info.PDoc != nil: switch { - case h.isPkg: + case info.IsPkg: title = "Package " + info.PDoc.PackageName case info.PDoc.PackageName == fakePkgName: // assume that the directory name is the command name diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index 943c81cfb..89b12b9ac 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -44,6 +44,7 @@ import ( "runtime" "strings" "time" + "url" ) const defaultAddr = ":6060" // default webserver address @@ -160,7 +161,7 @@ func loggingHandler(h http.Handler) http.Handler { } func remoteSearch(query string) (res *http.Response, err os.Error) { - search := "/search?f=text&q=" + http.URLEscape(query) + search := "/search?f=text&q=" + url.QueryEscape(query) // list of addresses to try var addrs []string @@ -401,9 +402,11 @@ func main() { fmt.Println() } if *html { - writeAnyHTML(os.Stdout, info.FSet, d) + var buf bytes.Buffer + writeNode(&buf, info.FSet, d) + FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil) } else { - writeAny(os.Stdout, info.FSet, d) + writeNode(os.Stdout, info.FSet, d) } fmt.Println() } diff --git a/src/cmd/godoc/snippet.go b/src/cmd/godoc/snippet.go index 626b01455..68e27d9a0 100755 --- a/src/cmd/godoc/snippet.go +++ b/src/cmd/godoc/snippet.go @@ -18,7 +18,7 @@ import ( type Snippet struct { Line int - Text []byte + Text string // HTML-escaped } func newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { @@ -30,7 +30,7 @@ func newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { buf2.WriteString("
")
 	FormatText(&buf2, buf1.Bytes(), -1, true, id.Name, nil)
 	buf2.WriteString("
") - return &Snippet{fset.Position(id.Pos()).Line, buf2.Bytes()} + return &Snippet{fset.Position(id.Pos()).Line, buf2.String()} } func findSpec(list []ast.Spec, id *ast.Ident) ast.Spec { @@ -94,10 +94,7 @@ func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) (s *Snippet) if s == nil { var buf bytes.Buffer fmt.Fprintf(&buf, `could not generate a snippet for %s`, id.Name) - s = &Snippet{ - fset.Position(id.Pos()).Line, - buf.Bytes(), - } + s = &Snippet{fset.Position(id.Pos()).Line, buf.String()} } return } diff --git a/src/cmd/gofix/Makefile b/src/cmd/gofix/Makefile index 7ce21e8aa..22033d7f8 100644 --- a/src/cmd/gofix/Makefile +++ b/src/cmd/gofix/Makefile @@ -23,6 +23,7 @@ GOFILES=\ sortslice.go\ stringssplit.go\ typecheck.go\ + url.go\ include ../../Make.cmd diff --git a/src/cmd/gofix/fix.go b/src/cmd/gofix/fix.go index c1c5a746c..cc85ceafa 100644 --- a/src/cmd/gofix/fix.go +++ b/src/cmd/gofix/fix.go @@ -71,17 +71,21 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) { walkBeforeAfter(*n, before, after) case **ast.FuncType: walkBeforeAfter(*n, before, after) + case **ast.Ident: + walkBeforeAfter(*n, before, after) // pointers to slices - case *[]ast.Stmt: + case *[]ast.Decl: walkBeforeAfter(*n, before, after) case *[]ast.Expr: walkBeforeAfter(*n, before, after) - case *[]ast.Decl: + case *[]*ast.File: + walkBeforeAfter(*n, before, after) + case *[]*ast.Ident: walkBeforeAfter(*n, before, after) case *[]ast.Spec: walkBeforeAfter(*n, before, after) - case *[]*ast.File: + case *[]ast.Stmt: walkBeforeAfter(*n, before, after) // These are ordered and grouped to match ../../pkg/go/ast/ast.go @@ -212,6 +216,7 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) { case *ast.ValueSpec: walkBeforeAfter(&n.Type, before, after) walkBeforeAfter(&n.Values, before, after) + walkBeforeAfter(&n.Names, before, after) case *ast.TypeSpec: walkBeforeAfter(&n.Type, before, after) @@ -245,6 +250,10 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) { for i := range n { walkBeforeAfter(&n[i], before, after) } + case []*ast.Ident: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } case []ast.Stmt: for i := range n { walkBeforeAfter(&n[i], before, after) diff --git a/src/cmd/gofix/url.go b/src/cmd/gofix/url.go new file mode 100644 index 000000000..c1e47bd4e --- /dev/null +++ b/src/cmd/gofix/url.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. + +package main + +import ( + "fmt" + "os" + "go/ast" +) + +var _ fmt.Stringer +var _ os.Error + +var urlFix = fix{ + "url", + url, + `Move the URL pieces of package http into a new package, url. + +http://codereview.appspot.com/4893043 +`, +} + +func init() { + register(urlFix) +} + +var urlRenames = []struct{ in, out string }{ + {"ParseURL", "Parse"}, + {"ParseURLReference", "ParseWithReference"}, + {"ParseQuery", "ParseQuery"}, + {"Values", "Values"}, + {"URLEscape", "QueryEscape"}, + {"URLUnescape", "QueryUnescape"}, + {"URLError", "Error"}, + {"URLEscapeError", "EscapeError"}, +} + +func url(f *ast.File) bool { + if imports(f, "url") || !imports(f, "http") { + return false + } + + fixed := false + + // Update URL code. + urlWalk := func(n interface{}) { + // Is it an identifier? + if ident, ok := n.(*ast.Ident); ok && ident.Name == "url" { + ident.Name = "url_" + return + } + // Parameter and result names. + if fn, ok := n.(*ast.FuncType); ok { + fixed = urlDoFields(fn.Params) || fixed + fixed = urlDoFields(fn.Results) || fixed + } + } + + // Fix up URL code and add import, at most once. + fix := func() { + if fixed { + return + } + walk(f, urlWalk) + addImport(f, "url") + fixed = true + } + + walk(f, func(n interface{}) { + // Rename functions and methods. + if expr, ok := n.(ast.Expr); ok { + for _, s := range urlRenames { + if isPkgDot(expr, "http", s.in) { + fix() + expr.(*ast.SelectorExpr).X.(*ast.Ident).Name = "url" + expr.(*ast.SelectorExpr).Sel.Name = s.out + return + } + } + } + }) + + // Remove the http import if no longer needed. + if fixed && !usesImport(f, "http") { + deleteImport(f, "http") + } + + return fixed +} + +func urlDoFields(list *ast.FieldList) (fixed bool) { + if list == nil { + return + } + for _, field := range list.List { + for _, ident := range field.Names { + if ident.Name == "url" { + fixed = true + ident.Name = "url_" + } + } + } + return +} diff --git a/src/cmd/gofix/url_test.go b/src/cmd/gofix/url_test.go new file mode 100644 index 000000000..1a7095a5d --- /dev/null +++ b/src/cmd/gofix/url_test.go @@ -0,0 +1,147 @@ +// 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 + +func init() { + addTestCases(urlTests) +} + +var urlTests = []testCase{ + { + Name: "url.0", + In: `package main + +import ( + "http" +) + +func f() { + http.ParseURL(a) + http.ParseURLReference(a) + http.ParseQuery(a) + m := http.Values{a: b} + http.URLEscape(a) + http.URLUnescape(a) + var x http.URLError + var y http.URLEscapeError +} +`, + Out: `package main + +import "url" + +func f() { + url.Parse(a) + url.ParseWithReference(a) + url.ParseQuery(a) + m := url.Values{a: b} + url.QueryEscape(a) + url.QueryUnescape(a) + var x url.Error + var y url.EscapeError +} +`, + }, + { + Name: "url.1", + In: `package main + +import ( + "http" +) + +func f() { + http.ParseURL(a) + var x http.Request +} +`, + Out: `package main + +import ( + "http" + "url" +) + +func f() { + url.Parse(a) + var x http.Request +} +`, + }, + { + Name: "url.2", + In: `package main + +import ( + "http" +) + +func f() { + http.ParseURL(a) + var url = 23 + url, x := 45, y +} + +func g(url string) string { + return url +} + +func h() (url string) { + return url +} +`, + Out: `package main + +import "url" + +func f() { + url.Parse(a) + var url_ = 23 + url_, x := 45, y +} + +func g(url_ string) string { + return url_ +} + +func h() (url_ string) { + return url_ +} +`, + }, + { + Name: "url.3", + In: `package main + +import "http" + +type U struct{ url string } + +func f() { + var u U + u.url = "x" +} + +func (url *T) m() string { + return url +} +`, + Out: `package main + +import "http" + +type U struct{ url string } + +func f() { + var u U + u.url = "x" +} + +func (url *T) m() string { + return url +} +`, + }, +} diff --git a/src/cmd/gofmt/test.sh b/src/cmd/gofmt/test.sh index 3e63d0c26..063a0727f 100755 --- a/src/cmd/gofmt/test.sh +++ b/src/cmd/gofmt/test.sh @@ -43,7 +43,7 @@ apply1() { bug226.go | bug228.go | bug248.go | bug274.go | bug280.go | \ bug282.go | bug287.go | bug298.go | bug299.go | bug300.go | \ bug302.go | bug306.go | bug322.go | bug324.go | bug335.go | \ - bug340.go | bug349.go | bug351.go ) return ;; + bug340.go | bug349.go | bug351.go | bug358.go ) return ;; esac # the following directories are skipped because they contain test # cases for syntax errors and thus won't parse in the first place: diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go index 86e490e24..910ab7090 100644 --- a/src/cmd/goinstall/main.go +++ b/src/cmd/goinstall/main.go @@ -196,9 +196,17 @@ func install(pkg, parent string) { } // Download remote packages if not found or forced with -u flag. remote, public := isRemote(pkg), false - if remote && (err == build.ErrNotFound || (err == nil && *update)) { - printf("%s: download\n", pkg) - public, err = download(pkg, tree.SrcDir()) + if remote { + if err == build.ErrNotFound || (err == nil && *update) { + // Download remote package. + printf("%s: download\n", pkg) + public, err = download(pkg, tree.SrcDir()) + } else { + // Test if this is a public repository + // (for reporting to dashboard). + m, _ := findPublicRepo(pkg) + public = m != nil + } } if err != nil { errorf("%s: %v\n", pkg, err) diff --git a/src/cmd/goinstall/make.go b/src/cmd/goinstall/make.go index 25f79d60d..38a70ddfd 100644 --- a/src/cmd/goinstall/make.go +++ b/src/cmd/goinstall/make.go @@ -8,11 +8,11 @@ package main import ( "bytes" - "exp/template" "go/build" "os" "path/filepath" "strings" + "template" ) // domake builds the package in dir. diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 974c08708..e7269169e 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -687,7 +687,7 @@ addaddrplus(Sym *s, Sym *t, int32 add) return i; } -vlong +static vlong addaddrplus4(Sym *s, Sym *t, int32 add) { vlong i; diff --git a/src/pkg/Makefile b/src/pkg/Makefile index c824f508c..ac36aaf0c 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -82,8 +82,7 @@ DIRS=\ exp/gui/x11\ exp/norm\ exp/regexp/syntax\ - exp/template\ - exp/template/parse\ + exp/template/html\ expvar\ flag\ fmt\ @@ -130,6 +129,7 @@ DIRS=\ net/dict\ net/textproto\ netchan\ + old/template\ os\ os/signal\ os/user\ @@ -156,6 +156,7 @@ DIRS=\ syslog\ tabwriter\ template\ + template/parse\ testing\ testing/iotest\ testing/quick\ @@ -163,6 +164,7 @@ DIRS=\ time\ try\ unicode\ + url\ utf16\ utf8\ websocket\ diff --git a/src/pkg/asn1/asn1.go b/src/pkg/asn1/asn1.go index 2dcdcfea6..39b676b41 100644 --- a/src/pkg/asn1/asn1.go +++ b/src/pkg/asn1/asn1.go @@ -681,23 +681,21 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam } err = err1 return - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - switch val.Type().Kind() { - case reflect.Int: - parsedInt, err1 := parseInt(innerBytes) - if err1 == nil { - val.SetInt(int64(parsedInt)) - } - err = err1 - return - case reflect.Int64: - parsedInt, err1 := parseInt64(innerBytes) - if err1 == nil { - val.SetInt(parsedInt) - } - err = err1 - return + case reflect.Int, reflect.Int32: + parsedInt, err1 := parseInt(innerBytes) + if err1 == nil { + val.SetInt(int64(parsedInt)) } + err = err1 + return + case reflect.Int64: + parsedInt, err1 := parseInt64(innerBytes) + if err1 == nil { + val.SetInt(parsedInt) + } + err = err1 + return + // TODO(dfc) Add support for the remaining integer types case reflect.Struct: structType := fieldType @@ -760,7 +758,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam } return } - err = StructuralError{"unknown Go type"} + err = StructuralError{"unsupported: " + v.Type().String()} return } @@ -787,7 +785,7 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // Because Unmarshal uses the reflect package, the structs // being written to must use upper case field names. // -// An ASN.1 INTEGER can be written to an int or int64. +// An ASN.1 INTEGER can be written to an int, int32 or int64. // If the encoded value does not fit in the Go type, // Unmarshal returns a parse error. // diff --git a/src/pkg/asn1/asn1_test.go b/src/pkg/asn1/asn1_test.go index 8487a14b6..9f48f7bdd 100644 --- a/src/pkg/asn1/asn1_test.go +++ b/src/pkg/asn1/asn1_test.go @@ -42,6 +42,37 @@ func TestParseInt64(t *testing.T) { } } +type int32Test struct { + in []byte + ok bool + out int32 +} + +var int32TestData = []int32Test{ + {[]byte{0x00}, true, 0}, + {[]byte{0x7f}, true, 127}, + {[]byte{0x00, 0x80}, true, 128}, + {[]byte{0x01, 0x00}, true, 256}, + {[]byte{0x80}, true, -128}, + {[]byte{0xff, 0x7f}, true, -129}, + {[]byte{0xff, 0xff, 0xff, 0xff}, true, -1}, + {[]byte{0xff}, true, -1}, + {[]byte{0x80, 0x00, 0x00, 0x00}, true, -2147483648}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00}, false, 0}, +} + +func TestParseInt32(t *testing.T) { + for i, test := range int32TestData { + ret, err := parseInt(test.in) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if test.ok && int32(ret) != test.out { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } +} + var bigIntTests = []struct { in []byte base10 string diff --git a/src/pkg/builtin/builtin.go b/src/pkg/builtin/builtin.go new file mode 100644 index 000000000..07acce4f7 --- /dev/null +++ b/src/pkg/builtin/builtin.go @@ -0,0 +1,135 @@ +// 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 builtin provides documentation for Go's built-in functions. + The functions documented here are not actually in package builtin + but their descriptions here allow godoc to present documentation + for the language's special functions. +*/ +package builtin + +// Type is here for the purposes of documentation only. It is a stand-in +// for any Go type, but represents the same type for any given function +// invocation. +type Type int + +// IntegerType is here for the purposes of documentation only. It is a stand-in +// for any integer type: int, uint, int8 etc. +type IntegerType int + +// FloatType is here for the purposes of documentation only. It is a stand-in +// for either float type: float32 or float64. +type FloatType int + +// ComplexType is here for the purposes of documentation only. It is a +// stand-in for either complex type: complex64 or complex128. +type ComplexType int + +// The append built-in function appends elements to the end of a slice. If +// it has sufficient capacity, the destination is resliced to accommodate the +// new elements. If it does not, a new underlying array will be allocated. +// Append returns the updated slice. It is therefore necessary to store the +// result of append, often in the variable holding the slice itself: +// slice = append(slice, elem1, elem2) +// slice = append(slice, anotherSlice...) +func append(slice []Type, elems ...Type) []Type + +// The copy built-in function copies elements from a source slice into a +// destination slice. (As a special case, it also will copy bytes from a +// string to a slice of bytes.) The source and destination may overlap. Copy +// returns the number of elements copied, which will be the minimum of +// len(src) and len(dst). +func copy(dst, src []Type) int + +// The len built-in function returns the length of v, according to its type: +// Array: the number of elements in v. +// Pointer to array: the number of elements in *v (even if v is nil). +// Slice, or map: the number of elements in v; if v is nil, len(v) is zero. +// String: the number of bytes in v. +// Channel: the number of elements queued (unread) in the channel buffer; +// if v is nil, len(v) is zero. +func len(v Type) int + +// The cap built-in function returns the capacity of v, according to its type: +// Array: the number of elements in v (same as len(v)). +// Pointer to array: the number of elements in *v (same as len(v)). +// Slice: the maximum length the slice can reach when resliced; +// if v is nil, cap(v) is zero. +// Channel: the channel buffer capacity, in units of elements; +// if v is nil, cap(v) is zero. +func cap(v Type) int + +// The make built-in function allocates and initializes an object of type +// slice, map, or chan (only). Like new, the first argument is a type, not a +// value. Unlike new, make's return type is the same as the type of its +// argument, not a pointer to it. The specification of the result depends on +// the type: +// Slice: The size specifies the length. The capacity of the slice is +// equal to its length. A second integer argument may be provided to +// specify a different capacity; it must be no smaller than the +// length, so make([]int, 0, 10) allocates a slice of length 0 and +// capacity 10. +// Map: An initial allocation is made according to the size but the +// resulting map has length 0. The size may be omitted, in which case +// a small starting size is allocated. +// Channel: The channel's buffer is initialized with the specified +// buffer capacity. If zero, or the size is omitted, the channel is +// unbuffered. +func make(Type, size IntegerType) Type + +// The new built-in function allocates memory. The first argument is a type, +// not a value, and the value returned is a pointer to a newly +// allocated zero value of that type. +func new(Type) *Type + +// The complex built-in function constructs a complex value from two +// floating-point values. The real and imaginary parts must be of the same +// size, either float32 or float64 (or assignable to them), and the return +// value will be the corresponding complex type (complex64 for float32, +// complex128 for float64). +func complex(r, i FloatType) ComplexType + +// The real built-in function returns the real part of the complex number c. +// The return value will be floating point type corresponding to the type of c. +func real(c ComplexType) FloatType + +// The imaginary built-in function returns the imaginary part of the complex +// number c. The return value will be floating point type corresponding to +// the type of c. +func imag(c ComplexType) FloatType + +// The close built-in function closes a channel, which must be either +// bidirectional or send-only. It should be executed only by the sender, +// never the receiver, and has the effect of shutting down the channel after +// the last sent value is received. After the last value has been received +// from a closed channel c, any receive from c will succeed without +// blocking, returning the zero value for the channel element. The form +// x, ok := <-c +// will also set ok to false for a closed channel. +func close(c chan<- Type) + +// The panic built-in function stops normal execution of the current +// goroutine. When a function F calls panic, normal execution of F stops +// immediately. Any functions whose execution was deferred by F are run in +// the usual way, and then F returns to its caller. To the caller G, the +// invocation of F then behaves like a call to panic, terminating G's +// execution and running any deferred functions. This continues until all +// functions in the executing goroutine have stopped, in reverse order. At +// that point, the program is terminated and the error condition is reported, +// including the value of the argument to panic. This termination sequence +// is called panicking and can be controlled by the built-in function +// recover. +func panic(v interface{}) + +// The recover built-in function allows a program to manage behavior of a +// panicking goroutine. Executing a call to recover inside a deferred +// function (but not any function called by it) stops the panicking sequence +// by restoring normal execution and retrieves the error value passed to the +// call of panic. If recover is called outside the deferred function it will +// not stop a panicking sequence. In this case, or when the goroutine is not +// panicking, or if the argument supplied to panic was nil, recover returns +// nil. Thus the return value from recover reports whether the goroutine is +// panicking. +func recover() interface{} diff --git a/src/pkg/crypto/rand/Makefile b/src/pkg/crypto/rand/Makefile index d1321297d..d1a3d45e8 100644 --- a/src/pkg/crypto/rand/Makefile +++ b/src/pkg/crypto/rand/Makefile @@ -19,6 +19,9 @@ GOFILES_darwin=\ GOFILES_linux=\ rand_unix.go\ +GOFILES_openbsd=\ + rand_unix.go\ + GOFILES_windows=\ rand_windows.go\ diff --git a/src/pkg/exec/Makefile b/src/pkg/exec/Makefile index 90bb74b41..ba19d0e4d 100644 --- a/src/pkg/exec/Makefile +++ b/src/pkg/exec/Makefile @@ -17,6 +17,9 @@ GOFILES_darwin=\ GOFILES_linux=\ lp_unix.go\ +GOFILES_openbsd=\ + lp_unix.go\ + GOFILES_windows=\ lp_windows.go\ diff --git a/src/pkg/exp/norm/Makefile b/src/pkg/exp/norm/Makefile index 906661d28..a4dfb43f7 100644 --- a/src/pkg/exp/norm/Makefile +++ b/src/pkg/exp/norm/Makefile @@ -6,23 +6,35 @@ include ../../../Make.inc TARG=exp/norm GOFILES=\ + composition.go\ + forminfo.go\ + normalize.go\ tables.go\ + trie.go\ include ../../../Make.pkg -CLEANFILES+=maketables +CLEANFILES+=maketables maketesttables -maketables: maketables.go - $(GC) maketables.go +maketables: maketables.go triegen.go + $(GC) maketables.go triegen.go $(LD) -o maketables maketables.$O +maketesttables: maketesttables.go triegen.go + $(GC) maketesttables.go triegen.go + $(LD) -o maketesttables maketesttables.$O + tables: maketables ./maketables > tables.go gofmt -w tables.go +trietesttables: maketesttables + ./maketesttables > triedata_test.go + gofmt -w triedata_test.go + # Build (but do not run) maketables during testing, # just to make sure it still compiles. -testshort: maketables +testshort: maketables maketesttables # Downloads from www.unicode.org, so not part # of standard test scripts. diff --git a/src/pkg/exp/norm/composition.go b/src/pkg/exp/norm/composition.go new file mode 100644 index 000000000..b2d2abaf6 --- /dev/null +++ b/src/pkg/exp/norm/composition.go @@ -0,0 +1,344 @@ +// 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" + +const ( + maxCombiningChars = 30 + 2 // +2 to hold CGJ and Hangul overflow. + 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 +) + +// reorderBuffer is used to normalize a single segment. Characters inserted with +// 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. +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. + f formInfo +} + +// reset discards all characters from the buffer. +func (rb *reorderBuffer) reset() { + rb.nrune = 0 + rb.nbyte = 0 +} + +// flush appends the normalized segment to out and resets rb. +func (rb *reorderBuffer) flush(out []byte) []byte { + for i := 0; i < rb.nrune; i++ { + start := rb.rune[i].pos + end := start + rb.rune[i].size + out = append(out, rb.byte[start:end]...) + } + rb.reset() + return out +} + +// 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. +func (rb *reorderBuffer) insertOrdered(info runeInfo) bool { + n := rb.nrune + if n >= maxCombiningChars { + return false + } + b := rb.rune[:] + cc := info.ccc + if cc > 0 { + // Find insertion position + move elements to make room. + for ; n > 0; n-- { + if b[n-1].ccc <= cc { + break + } + b[n] = b[n-1] + } + } + rb.nrune += 1 + pos := uint8(rb.nbyte) + rb.nbyte += info.size + info.pos = pos + b[n] = info + return true +} + +// 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 + } + 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 !rb.insertOrdered(info) { + return false + } + } + copy(rb.byte[pos:], dcomp) + } else { + if !rb.insertOrdered(info) { + return false + } + copy(rb.byte[pos:], src[:info.size]) + } + return true +} + +// appendRune inserts a rune at the end of the buffer. It is used for Hangul. +func (rb *reorderBuffer) appendRune(rune uint32) { + bn := rb.nbyte + sz := utf8.EncodeRune(rb.byte[bn:], int(rune)) + rb.nbyte += uint8(sz) + 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 + 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. +func (rb *reorderBuffer) runeAt(n int) uint32 { + inf := rb.rune[n] + rune, _ := utf8.DecodeRune(rb.byte[inf.pos : inf.pos+inf.size]) + return uint32(rune) +} + +// bytesAt returns the UTF-8 encoding of the rune at position n. +// It is used for Hangul and recomposition. +func (rb *reorderBuffer) bytesAt(n int) []byte { + inf := rb.rune[n] + return rb.byte[inf.pos : int(inf.pos)+int(inf.size)] +} + +// For Hangul we combine algorithmically, instead of using tables. +const ( + hangulBase = 0xAC00 // UTF-8(hangulBase) -> EA B0 80 + hangulBase0 = 0xEA + hangulBase1 = 0xB0 + hangulBase2 = 0x80 + + hangulEnd = hangulBase + jamoLVTCount // UTF-8(0xD7A4) -> ED 9E A4 + hangulEnd0 = 0xED + hangulEnd1 = 0x9E + hangulEnd2 = 0xA4 + + jamoLBase = 0x1100 // UTF-8(jamoLBase) -> E1 84 00 + jamoLBase0 = 0xE1 + jamoLBase1 = 0x84 + jamoLEnd = 0x1113 + jamoVBase = 0x1161 + jamoVEnd = 0x1176 + jamoTBase = 0x11A7 + jamoTEnd = 0x11C3 + + jamoTCount = 28 + jamoVCount = 21 + jamoVTCount = 21 * 28 + jamoLVTCount = 19 * 21 * 28 +) + +// Caller must verify that len(b) >= 3. +func isHangul(b []byte) bool { + b0 := b[0] + if b0 < hangulBase0 { + return false + } + b1 := b[1] + switch { + case b0 == hangulBase0: + return b1 >= hangulBase1 + case b0 < hangulEnd0: + return true + case b0 > hangulEnd0: + return false + case b1 < hangulEnd1: + return true + } + return b1 == hangulEnd1 && b[2] < hangulEnd2 +} + +// Caller must verify that len(b) >= 3. +func isHangulString(b string) bool { + b0 := b[0] + if b0 < hangulBase0 { + return false + } + b1 := b[1] + switch { + case b0 == hangulBase0: + return b1 >= hangulBase1 + case b0 < hangulEnd0: + return true + case b0 > hangulEnd0: + return false + case b1 < hangulEnd1: + return true + } + return b1 == hangulEnd1 && b[2] < hangulEnd2 +} + +// Caller must ensure len(b) >= 2. +func isJamoVT(b []byte) bool { + // True if (rune & 0xff00) == jamoLBase + return b[0] == jamoLBase0 && (b[1]&0xFC) == jamoLBase1 +} + +func isHangulWithoutJamoT(b []byte) bool { + c, _ := utf8.DecodeRune(b) + c -= hangulBase + return c < jamoLVTCount && c%jamoTCount == 0 +} + +// decomposeHangul algorithmically decomposes a Hangul rune into +// its Jamo components. +// See http://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul. +func (rb *reorderBuffer) decomposeHangul(rune uint32) bool { + b := rb.rune[:] + n := rb.nrune + if n+3 > len(b) { + return false + } + rune -= hangulBase + x := rune % jamoTCount + rune /= jamoTCount + rb.appendRune(jamoLBase + rune/jamoVCount) + rb.appendRune(jamoVBase + rune%jamoVCount) + if x != 0 { + rb.appendRune(jamoTBase + x) + } + return true +} + +// 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 + b := rb.rune[:] + bn := rb.nrune + for s, i := 0, 1; i < bn; i++ { + cccB := b[k-1].ccc + cccC := b[i].ccc + if cccB == 0 { + s = k - 1 + } + if s != k-1 && cccB >= cccC { + // b[i] is blocked by greater-equal cccX below it + b[k] = b[i] + k++ + } else { + l := rb.runeAt(s) // also used to compare to hangulBase + v := rb.runeAt(i) // also used to compare to jamoT + switch { + case jamoLBase <= l && l < jamoLEnd && + jamoVBase <= v && v < jamoVEnd: + // 11xx plus 116x to LV + rb.assignRune(s, hangulBase+ + (l-jamoLBase)*jamoVTCount+(v-jamoVBase)*jamoTCount) + case hangulBase <= l && l < hangulEnd && + jamoTBase < v && v < jamoTEnd && + ((l-hangulBase)%jamoTCount) == 0: + // ACxx plus 11Ax to LVT + rb.assignRune(s, l+v-jamoTBase) + default: + b[k] = b[i] + k++ + } + } + } + rb.nrune = k +} + +// compose recombines the runes in the buffer. +// It should only be used to recompose a single segment, as it will not +// handle alternations between Hangul and non-Hangul characters correctly. +func (rb *reorderBuffer) compose() { + // UAX #15, section X5 , including Corrigendum #5 + // "In any character sequence beginning with starter S, a character C is + // 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." + 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() + return + } + ii := b[i] + // We can only use combineForward as a filter if we later + // get the info for the combined character. This is more + // expensive than using the filter. Using combinesBackward() + // is safe. + if ii.flags.combinesBackward() { + cccB := b[k-1].ccc + cccC := ii.ccc + blocked := false // b[i] blocked by starter or greater or equal CCC? + if cccB == 0 { + s = k - 1 + } else { + blocked = s != k-1 && cccB >= cccC + } + if !blocked { + combined := combine(rb.runeAt(s), rb.runeAt(i)) + if combined != 0 { + rb.assignRune(s, combined) + continue + } + } + } + b[k] = b[i] + k++ + } + rb.nrune = k +} diff --git a/src/pkg/exp/norm/composition_test.go b/src/pkg/exp/norm/composition_test.go new file mode 100644 index 000000000..195a0c1e8 --- /dev/null +++ b/src/pkg/exp/norm/composition_test.go @@ -0,0 +1,138 @@ +// 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 "testing" + +// TestCase is used for most tests. +type TestCase struct { + in []int + out []int +} + +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)) +} + +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) { + 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)) { + t.Errorf("%s:%d: insert failed for rune %d", name, i, j) + } + } + if rb.f.composing { + rb.compose() + } + if rb.nrune != len(test.out) { + t.Errorf("%s:%d: length = %d; want %d", name, i, rb.nrune, len(test.out)) + continue + } + for j, want := range test.out { + found := int(rb.runeAt(j)) + if found != want { + t.Errorf("%s:%d: runeAt(%d) = %U; want %U", name, i, j, found, want) + } + } + } +} + +func TestFlush(t *testing.T) { + rb := &reorderBuffer{f: *formTable[NFC]} + out := make([]byte, 0) + + out = rb.flush(out) + if len(out) != 0 { + t.Errorf("wrote bytes on flush of empty buffer. (len(out) = %d)", len(out)) + } + + for _, r := range []int("world!") { + insert(rb, r) + } + + out = []byte("Hello ") + out = rb.flush(out) + want := "Hello world!" + if string(out) != want { + t.Errorf(`output after flush was "%s"; want "%s"`, string(out), want) + } + if rb.nrune != 0 { + t.Errorf("flush: non-null size of info buffer (rb.nrune == %d)", rb.nrune) + } + if rb.nbyte != 0 { + t.Errorf("flush: non-null size of byte buffer (rb.nbyte == %d)", rb.nbyte) + } +} + +var insertTests = []TestCase{ + {[]int{'a'}, []int{'a'}}, + {[]int{0x300}, []int{0x300}}, + {[]int{0x300, 0x316}, []int{0x316, 0x300}}, // CCC(0x300)==230; CCC(0x316)==220 + {[]int{0x316, 0x300}, []int{0x316, 0x300}}, + {[]int{0x41, 0x316, 0x300}, []int{0x41, 0x316, 0x300}}, + {[]int{0x41, 0x300, 0x316}, []int{0x41, 0x316, 0x300}}, + {[]int{0x300, 0x316, 0x41}, []int{0x316, 0x300, 0x41}}, + {[]int{0x41, 0x300, 0x40, 0x316}, []int{0x41, 0x300, 0x40, 0x316}}, +} + +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) +} + +var decompositionNFDTest = []TestCase{ + {[]int{0xC0}, []int{0x41, 0x300}}, + {[]int{0xAC00}, []int{0x1100, 0x1161}}, + {[]int{0x01C4}, []int{0x01C4}}, + {[]int{0x320E}, []int{0x320E}}, + {[]int("음ẻ과"), []int{0x110B, 0x1173, 0x11B7, 0x65, 0x309, 0x1100, 0x116A}}, +} + +var decompositionNFKDTest = []TestCase{ + {[]int{0xC0}, []int{0x41, 0x300}}, + {[]int{0xAC00}, []int{0x1100, 0x1161}}, + {[]int{0x01C4}, []int{0x44, 0x5A, 0x030C}}, + {[]int{0x320E}, []int{0x28, 0x1100, 0x1161, 0x29}}, +} + +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) +} + +var compositionTest = []TestCase{ + {[]int{0x41, 0x300}, []int{0xC0}}, + {[]int{0x41, 0x316}, []int{0x41, 0x316}}, + {[]int{0x41, 0x300, 0x35D}, []int{0xC0, 0x35D}}, + {[]int{0x41, 0x316, 0x300}, []int{0xC0, 0x316}}, + // blocking starter + {[]int{0x41, 0x316, 0x40, 0x300}, []int{0x41, 0x316, 0x40, 0x300}}, + {[]int{0x1100, 0x1161}, []int{0xAC00}}, + // parenthesized Hangul, alternate between ASCII and Hangul. + {[]int{0x28, 0x1100, 0x1161, 0x29}, []int{0x28, 0xAC00, 0x29}}, +} + +func TestComposition(t *testing.T) { + rb := &reorderBuffer{f: *formTable[NFC]} + runTests(t, "TestComposition", rb, insert, compositionTest) +} diff --git a/src/pkg/exp/norm/forminfo.go b/src/pkg/exp/norm/forminfo.go new file mode 100644 index 000000000..ee3edb8ea --- /dev/null +++ b/src/pkg/exp/norm/forminfo.go @@ -0,0 +1,188 @@ +// 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 + +// This file contains Form-specific logic and wrappers for data in tables.go. + +type runeInfo struct { + pos uint8 // start position in reorderBuffer; used in composition.go + size uint8 // length of UTF-8 encoding of this rune + ccc uint8 // canonical combining class + flags qcInfo // quick check flags +} + +// 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 + +// formInfo holds Form-specific functions and tables. +type formInfo struct { + form Form + + composing, compatibility bool // form type + + decompose decompFunc + decomposeString decompFuncString + info lookupFunc + infoString lookupFuncString + boundaryBefore boundaryFunc + boundaryAfter boundaryFunc +} + +var formTable []*formInfo + +func init() { + formTable = make([]*formInfo, 4) + + for i := range formTable { + f := &formInfo{} + formTable[i] = f + f.form = Form(i) + 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 + f.boundaryBefore = compBoundaryBefore + f.boundaryAfter = compBoundaryAfter + } else { + f.boundaryBefore = decompBoundary + f.boundaryAfter = decompBoundary + } + } +} + +func decompBoundary(f *formInfo, info runeInfo) bool { + if info.ccc == 0 && info.flags.isYesD() { // Implies isHangul(b) == true + return true + } + // We assume that the CCC of the first character in a decomposition + // is always non-zero if different from info.ccc and that we can return + // false at this point. This is verified by maketables. + return false +} + +func compBoundaryBefore(f *formInfo, info runeInfo) bool { + if info.ccc == 0 && info.flags.isYesC() { + return true + } + // We assume that the CCC of the first character in a decomposition + // is always non-zero if different from info.ccc and that we can return + // false at this point. This is verified by maketables. + return false +} + +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() +} + +// We pack quick check data in 4 bits: +// 0: NFD_QC Yes (0) or No (1). No also means there is a decomposition. +// 1..2: NFC_QC Yes(00), No (01), or Maybe (11) +// 3: Combines forward (0 == false, 1 == true) +// +// When all 4 bits are zero, the character is inert, meaning it is never +// influenced by normalization. +// +// We pack the bits for both NFC/D and NFKC/D in one byte. +type qcInfo uint8 + +func (i qcInfo) isYesC() bool { return i&0x2 == 0 } +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 + +// 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) + n := decomps[p] + p++ + return decomps[p : p+uint16(n)] +} + +func decomposeStringNFKC(s string) []byte { + p := nfkcDecompTrie.lookupStringUnsafe(s) + n := decomps[p] + p++ + return decomps[p : p+uint16(n)] +} + +// Recomposition +// We use 32-bit keys instead of 64-bit for the two codepoint keys. +// This clips off the bits of three entries, but we know this will not +// result in a collision. In the unlikely event that changes to +// UnicodeData.txt introduce collisions, the compiler will catch it. +// Note that the recomposition map for NFC and NFKC are identical. + +// combine returns the combined rune or 0 if it doesn't exist. +func combine(a, b uint32) uint32 { + key := uint32(uint16(a))<<16 + uint32(uint16(b)) + return recompMap[key] +} + +// The 16-bit character info has the following bit layout: +// 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) + 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) + return runeInfo{0, uint8(sz), uint8(v), qcInfo(v >> 12)} +} diff --git a/src/pkg/exp/norm/maketables.go b/src/pkg/exp/norm/maketables.go index 0064b2cbe..e3e5700a6 100644 --- a/src/pkg/exp/norm/maketables.go +++ b/src/pkg/exp/norm/maketables.go @@ -12,7 +12,6 @@ import ( "bytes" "flag" "fmt" - "hash/crc32" "http" "io" "log" @@ -20,7 +19,6 @@ import ( "regexp" "strconv" "strings" - "utf8" ) func main() { @@ -118,7 +116,6 @@ const ( MNumberOfModes ) - // This contains only the properties we're interested in. type Char struct { name string @@ -269,7 +266,7 @@ func parseCharacter(line string) { } x, err = strconv.Atoui64(field[FCanonicalCombiningClass]) if err != nil { - logger.Fatal("%U: bad ccc field: %s", int(x), err) + logger.Fatalf("%U: bad ccc field: %s", int(x), err) } ccc := uint8(x) decmap := field[FDecompMapping] @@ -440,7 +437,6 @@ func decomposeRecursive(form int, rune int, d Decomposition) Decomposition { return d } - func completeCharFields(form int) { // Phase 0: pre-expand decomposition. for i := range chars { @@ -535,130 +531,6 @@ func completeCharFields(form int) { } } -// Intermediate trie structure -type trieNode struct { - table [256]*trieNode - value uint16 - b byte - leaf bool -} - -func newNode() *trieNode { - return new(trieNode) -} - -type nodeIndex struct { - lookupBlocks []*trieNode - valueBlocks []*trieNode - - lookupBlockIdx map[uint32]uint16 - valueBlockIdx map[uint32]uint16 -} - -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) - return index -} - -func (n trieNode) isInternal() bool { - internal := true - for i := 0; i < 256; i++ { - if nn := n.table[i]; nn != nil { - if !internal && !nn.leaf { - panic("Node contains both leaf and non-leaf children.") - } - internal = internal && !nn.leaf - } - } - return internal -} - -func (n *trieNode) insert(rune int, value uint16) { - var p [utf8.UTFMax]byte - sz := utf8.EncodeRune(p[:], rune) - - for i := 0; i < sz; i++ { - if n.leaf { - panic("Node should not be a leaf") - } - nn := n.table[int(p[i])] - if nn == nil { - nn = newNode() - nn.b = p[i] - n.table[int(p[i])] = nn - } - n = nn - } - n.value = value - n.leaf = true -} - -func computeOffsets(index *nodeIndex, n *trieNode) uint16 { - 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 - if nn := n.table[0x80+i]; nn != nil { - v = computeOffsets(index, nn) - } - hasher.Write([]byte{uint8(v >> 8), uint8(v)}) - } - h := hasher.Sum32() - if n.isInternal() { - v, ok := index.lookupBlockIdx[h] - if !ok { - v = uint16(len(index.lookupBlocks)) - index.lookupBlocks = append(index.lookupBlocks, n) - index.lookupBlockIdx[h] = v - } - n.value = v - } else { - v, ok := index.valueBlockIdx[h] - if !ok { - v = uint16(len(index.valueBlocks)) - index.valueBlocks = append(index.valueBlocks, n) - index.valueBlockIdx[h] = v - } - n.value = v - } - return n.value -} - -func printValueBlock(nr int, n *trieNode, offset int) { - fmt.Printf("\n// Block %X", nr) - for i := 0; i < 64; i++ { - if i%8 == 0 { - fmt.Printf("\n") - } - var v uint16 = 0 - if nn := n.table[i+offset]; nn != nil { - v = nn.value - } - fmt.Printf("0x%.4X, ", v) - } -} - -func printLookupBlock(nr int, n *trieNode, offset int) { - fmt.Printf("\n// Block %X", nr) - for i := 0; i < 64; i++ { - if i%8 == 0 { - fmt.Printf("\n") - } - var v uint16 = 0 - if nn := n.table[i+offset]; nn != nil { - v = nn.value - } - fmt.Printf("0x%.2X, ", v) - } -} - func printBytes(b []byte, name string) { fmt.Printf("// %s: %d bytes\n", name, len(b)) fmt.Printf("var %s = [...]byte {", name) @@ -674,48 +546,6 @@ func printBytes(b []byte, name string) { fmt.Print("\n}\n\n") } -// printTrieTables returns the size of the generated tables. -func printTrieTables(t *trieNode, name string) int { - index := newIndex() - // Directly add first 128 values of UTF-8, followed by nil block. - index.valueBlocks = append(index.valueBlocks, nil, nil, nil) - // First byte of multi-byte UTF-8 codepoints are indexed in 4th block. - index.lookupBlocks = append(index.lookupBlocks, nil, nil, nil, nil) - // Index starter bytes of multi-byte UTF-8. - for i := 0xC0; i < 0x100; i++ { - if t.table[i] != nil { - computeOffsets(index, t.table[i]) - } - } - - nv := len(index.valueBlocks) * 64 - - 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 = [...]uint16 {", name) - printValueBlock(0, t, 0) - printValueBlock(1, t, 64) - printValueBlock(2, newNode(), 0) - for i := 3; i < len(index.valueBlocks); i++ { - printValueBlock(i, index.valueBlocks[i], 0x80) - } - fmt.Print("\n}\n\n") - - ni := len(index.lookupBlocks) * 64 - fmt.Printf("// %sLookup: %d bytes\n", name, ni) - fmt.Printf("// Block 0 is the null block.\n") - fmt.Printf("var %sLookup = [...]uint8 {", name) - printLookupBlock(0, newNode(), 0) - printLookupBlock(1, newNode(), 0) - printLookupBlock(2, newNode(), 0) - printLookupBlock(3, t, 0xC0) - for i := 4; i < len(index.lookupBlocks); i++ { - printLookupBlock(i, index.lookupBlocks[i], 0x80) - } - fmt.Print("\n}\n\n") - return nv*2 + ni -} - // See forminfo.go for format. func makeEntry(f *FormInfo) uint16 { e := uint16(0) @@ -757,7 +587,7 @@ func printCharInfoTables() int { t.insert(i, v) } } - return printTrieTables(t, "charInfo") + return t.printTables("charInfo") } func printDecompositionTables() int { @@ -791,14 +621,26 @@ func printDecompositionTables() int { d := c.forms[FCanonical].expandedDecomp if len(d) != 0 { nfcT.insert(i, positionMap[string([]int(d))]) + if ccc(c.codePoint) != ccc(d[0]) { + // We assume the lead ccc of a decomposition is !=0 in this case. + if ccc(d[0]) == 0 { + logger.Fatal("Expected differing CCC to be non-zero.") + } + } } d = c.forms[FCompatibility].expandedDecomp if len(d) != 0 { nfkcT.insert(i, positionMap[string([]int(d))]) + if ccc(c.codePoint) != ccc(d[0]) { + // We assume the lead ccc of a decomposition is !=0 in this case. + if ccc(d[0]) == 0 { + logger.Fatal("Expected differing CCC to be non-zero.") + } + } } } - size += printTrieTables(nfcT, "nfcDecomp") - size += printTrieTables(nfkcT, "nfkcDecomp") + size += nfcT.printTables("nfcDecomp") + size += nfkcT.printTables("nfkcDecomp") return size } diff --git a/src/pkg/exp/norm/maketesttables.go b/src/pkg/exp/norm/maketesttables.go new file mode 100644 index 000000000..c5f6a6436 --- /dev/null +++ b/src/pkg/exp/norm/maketesttables.go @@ -0,0 +1,42 @@ +// 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. + +// Generate test data for trie code. + +package main + +import ( + "fmt" +) + +func main() { + printTestTables() +} + +// We take the smallest, largest and an arbitrary value for each +// of the UTF-8 sequence lengths. +var testRunes = []int{ + 0x01, 0x0C, 0x7F, // 1-byte sequences + 0x80, 0x100, 0x7FF, // 2-byte sequences + 0x800, 0x999, 0xFFFF, // 3-byte sequences + 0x10000, 0x10101, 0x10FFFF, // 4-byte sequences +} + +const fileHeader = `// Generated by running +// maketesttables +// DO NOT EDIT + +package norm + +` + +func printTestTables() { + fmt.Print(fileHeader) + fmt.Printf("var testRunes = %#v\n\n", testRunes) + t := newNode() + for i, r := range testRunes { + t.insert(r, uint16(i)) + } + t.printTables("testdata") +} diff --git a/src/pkg/exp/norm/normalize.go b/src/pkg/exp/norm/normalize.go index 81311bfcb..e9d18dd9e 100644 --- a/src/pkg/exp/norm/normalize.go +++ b/src/pkg/exp/norm/normalize.go @@ -31,45 +31,69 @@ const ( ) // Bytes returns f(b). May return b if f(b) = b. -func (f Form) Bytes(b []byte) []byte +func (f Form) Bytes(b []byte) []byte { + panic("not implemented") +} // String returns f(s). -func (f Form) String(s string) string +func (f Form) String(s string) string { + panic("not implemented") +} // IsNormal returns true if b == f(b). -func (f Form) IsNormal(b []byte) bool +func (f Form) IsNormal(b []byte) bool { + panic("not implemented") +} // IsNormalString returns true if s == f(s). -func (f Form) IsNormalString(s string) bool +func (f Form) IsNormalString(s string) bool { + panic("not implemented") +} // 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 +func (f Form) Append(out, b []byte) []byte { + panic("not implemented") +} // 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 +func (f Form) AppendString(out []byte, s string) []byte { + panic("not implemented") +} // 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 +func (f Form) QuickSpan(b []byte) int { + panic("not implemented") +} // 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 +func (f Form) QuickSpanString(s string) int { + panic("not implemented") +} // 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) +func (f Form) FirstBoundary(b []byte) (i int, ok bool) { + panic("not implemented") +} // 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) +func (f Form) FirstBoundaryInString(s string) (i int, ok bool) { + panic("not implemented") +} // 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) +func (f Form) LastBoundary(b []byte) (i int, ok bool) { + panic("not implemented") +} // 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) +func (f Form) LastBoundaryInString(s string) (i int, ok bool) { + panic("not implemented") +} diff --git a/src/pkg/exp/norm/tables.go b/src/pkg/exp/norm/tables.go index 7addc4771..76995c2fa 100644 --- a/src/pkg/exp/norm/tables.go +++ b/src/pkg/exp/norm/tables.go @@ -2492,2409 +2492,1935 @@ var decomps = [...]byte{ // nfcDecompValues: 4992 entries, 9984 bytes // Block 2 is the null block. -var nfcDecompValues = [...]uint16{ - // Block 0 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3 - 0x0032, 0x0036, 0x003A, 0x003E, 0x0042, 0x0046, 0x0000, 0x004A, - 0x004E, 0x0052, 0x0056, 0x005A, 0x005E, 0x0062, 0x0066, 0x006A, - 0x0000, 0x006E, 0x0072, 0x0076, 0x007A, 0x007E, 0x0082, 0x0000, - 0x0000, 0x0086, 0x008A, 0x008E, 0x0092, 0x0096, 0x0000, 0x0000, - 0x009A, 0x009E, 0x00A2, 0x00A6, 0x00AA, 0x00AE, 0x0000, 0x00B2, - 0x00B6, 0x00BA, 0x00BE, 0x00C2, 0x00C6, 0x00CA, 0x00CE, 0x00D2, - 0x0000, 0x00D6, 0x00DA, 0x00DE, 0x00E2, 0x00E6, 0x00EA, 0x0000, - 0x0000, 0x00EE, 0x00F2, 0x00F6, 0x00FA, 0x00FE, 0x0000, 0x0102, - // Block 4 - 0x0106, 0x010A, 0x010E, 0x0112, 0x0116, 0x011A, 0x011E, 0x0122, - 0x0126, 0x012A, 0x012E, 0x0132, 0x0136, 0x013A, 0x013E, 0x0142, - 0x0000, 0x0000, 0x0146, 0x014A, 0x014E, 0x0152, 0x0156, 0x015A, - 0x015E, 0x0162, 0x0166, 0x016A, 0x016E, 0x0172, 0x0176, 0x017A, - 0x017E, 0x0182, 0x0186, 0x018A, 0x018E, 0x0192, 0x0000, 0x0000, - 0x0196, 0x019A, 0x019E, 0x01A2, 0x01A6, 0x01AA, 0x01AE, 0x01B2, - 0x01B6, 0x0000, 0x0000, 0x0000, 0x01C0, 0x01C4, 0x01C8, 0x01CC, - 0x0000, 0x01D0, 0x01D4, 0x01D8, 0x01DC, 0x01E0, 0x01E4, 0x0000, - // Block 5 - 0x0000, 0x0000, 0x0000, 0x01F0, 0x01F4, 0x01F8, 0x01FC, 0x0200, - 0x0204, 0x0000, 0x0000, 0x0000, 0x020C, 0x0210, 0x0214, 0x0218, - 0x021C, 0x0220, 0x0000, 0x0000, 0x0224, 0x0228, 0x022C, 0x0230, - 0x0234, 0x0238, 0x023C, 0x0240, 0x0244, 0x0248, 0x024C, 0x0250, - 0x0254, 0x0258, 0x025C, 0x0260, 0x0264, 0x0268, 0x0000, 0x0000, - 0x026C, 0x0270, 0x0274, 0x0278, 0x027C, 0x0280, 0x0284, 0x0288, - 0x028C, 0x0290, 0x0294, 0x0298, 0x029C, 0x02A0, 0x02A4, 0x02A8, - 0x02AC, 0x02B0, 0x02B4, 0x02B8, 0x02BC, 0x02C0, 0x02C4, 0x0000, - // Block 6 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x02CA, 0x02CE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x02D2, - 0x02D6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 7 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x02FB, 0x02FF, 0x0303, - 0x0307, 0x030B, 0x030F, 0x0313, 0x0317, 0x031B, 0x0321, 0x0327, - 0x032D, 0x0333, 0x0339, 0x033F, 0x0345, 0x0000, 0x034B, 0x0351, - 0x0357, 0x035D, 0x0363, 0x0368, 0x0000, 0x0000, 0x036D, 0x0371, - 0x0375, 0x0379, 0x037D, 0x0381, 0x0385, 0x038B, 0x0391, 0x0396, - 0x039B, 0x0000, 0x0000, 0x0000, 0x03A8, 0x03AC, 0x0000, 0x0000, - 0x03B0, 0x03B4, 0x03B8, 0x03BE, 0x03C4, 0x03C9, 0x03CE, 0x03D3, - // Block 8 - 0x03D8, 0x03DC, 0x03E0, 0x03E4, 0x03E8, 0x03EC, 0x03F0, 0x03F4, - 0x03F8, 0x03FC, 0x0400, 0x0404, 0x0408, 0x040C, 0x0410, 0x0414, - 0x0418, 0x041C, 0x0420, 0x0424, 0x0428, 0x042C, 0x0430, 0x0434, - 0x0438, 0x043C, 0x0440, 0x0444, 0x0000, 0x0000, 0x0448, 0x044C, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0450, 0x0454, - 0x0458, 0x045C, 0x0460, 0x0466, 0x046C, 0x0472, 0x0478, 0x047C, - 0x0480, 0x0486, 0x048C, 0x0490, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 9 - 0x04CC, 0x04CF, 0x0000, 0x04D2, 0x04D5, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x04DA, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x04E1, 0x0000, - // Block A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x04E3, 0x04EE, 0x04F3, - 0x04F6, 0x04FB, 0x0500, 0x0000, 0x0505, 0x0000, 0x050A, 0x050F, - 0x0514, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x051B, 0x0520, 0x0525, 0x052A, 0x052F, 0x0534, - 0x0539, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0540, 0x0545, 0x054A, 0x054F, 0x0554, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0562, 0x0567, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block C - 0x0584, 0x0589, 0x0000, 0x058E, 0x0000, 0x0000, 0x0000, 0x0593, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0598, 0x059D, 0x05A2, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x05A7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x05AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x05B1, 0x05B6, 0x0000, 0x05BB, 0x0000, 0x0000, 0x0000, 0x05C0, - 0x0000, 0x0000, 0x0000, 0x0000, 0x05C5, 0x05CA, 0x05CF, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x05D4, 0x05D9, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block E - 0x0000, 0x05DE, 0x05E3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x05E8, 0x05ED, 0x05F2, 0x05F7, 0x0000, 0x0000, 0x05FC, 0x0601, - 0x0000, 0x0000, 0x0606, 0x060B, 0x0610, 0x0615, 0x061A, 0x061F, - 0x0000, 0x0000, 0x0624, 0x0629, 0x062E, 0x0633, 0x0638, 0x063D, - 0x0000, 0x0000, 0x0642, 0x0647, 0x064C, 0x0651, 0x0656, 0x065B, - 0x0660, 0x0665, 0x066A, 0x066F, 0x0674, 0x0679, 0x0000, 0x0000, - 0x067E, 0x0683, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block F - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x068D, 0x0692, 0x0697, 0x069C, 0x06A1, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 10 - 0x06BA, 0x0000, 0x06BF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x06C4, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 11 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x06C9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x06D0, 0x0000, 0x0000, 0x06D7, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 12 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x06DE, 0x06E5, 0x06EC, 0x06F3, 0x06FA, 0x0701, 0x0708, 0x070F, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 13 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0716, 0x071D, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0724, 0x072B, 0x0000, 0x0732, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 14 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0739, 0x0000, 0x0000, 0x0740, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 15 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0747, 0x074E, 0x0755, 0x0000, 0x0000, 0x075C, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 16 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0763, 0x0000, 0x0000, 0x076A, 0x0771, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0778, 0x077F, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 17 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0786, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 18 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x078D, 0x0794, 0x079B, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 19 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x07A2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1A - 0x07A9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x07B0, - 0x07B7, 0x0000, 0x07BE, 0x07C5, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x07CF, 0x07D6, 0x07DD, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x07E4, 0x0000, 0x07EB, 0x07F2, 0x07FC, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1D - 0x0000, 0x0000, 0x0000, 0x0823, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x082A, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0831, 0x0000, 0x0000, 0x0000, 0x0000, 0x0838, - 0x0000, 0x0000, 0x0000, 0x0000, 0x083F, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0846, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x084D, 0x0000, 0x0854, 0x085B, 0x0000, - 0x086C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1E - 0x0000, 0x087D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0884, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x088B, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0892, 0x0000, 0x0000, 0x0000, 0x0000, 0x0899, - 0x0000, 0x0000, 0x0000, 0x0000, 0x08A0, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x08A7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1F - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x08AE, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 20 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x08B9, 0x0000, - 0x08C0, 0x0000, 0x08C7, 0x0000, 0x08CE, 0x0000, 0x08D5, 0x0000, - 0x0000, 0x0000, 0x08DC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x08E3, 0x0000, 0x08EA, 0x0000, 0x0000, - // Block 21 - 0x08F1, 0x08F8, 0x0000, 0x08FF, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 22 - 0x09EA, 0x09EE, 0x09F2, 0x09F6, 0x09FA, 0x09FE, 0x0A02, 0x0A06, - 0x0A0A, 0x0A10, 0x0A16, 0x0A1A, 0x0A1E, 0x0A22, 0x0A26, 0x0A2A, - 0x0A2E, 0x0A32, 0x0A36, 0x0A3A, 0x0A3E, 0x0A44, 0x0A4A, 0x0A50, - 0x0A56, 0x0A5A, 0x0A5E, 0x0A62, 0x0A66, 0x0A6C, 0x0A72, 0x0A76, - 0x0A7A, 0x0A7E, 0x0A82, 0x0A86, 0x0A8A, 0x0A8E, 0x0A92, 0x0A96, - 0x0A9A, 0x0A9E, 0x0AA2, 0x0AA6, 0x0AAA, 0x0AAE, 0x0AB2, 0x0AB8, - 0x0ABE, 0x0AC2, 0x0AC6, 0x0ACA, 0x0ACE, 0x0AD2, 0x0AD6, 0x0ADA, - 0x0ADE, 0x0AE4, 0x0AEA, 0x0AEE, 0x0AF2, 0x0AF6, 0x0AFA, 0x0AFE, - // Block 23 - 0x0B02, 0x0B06, 0x0B0A, 0x0B0E, 0x0B12, 0x0B16, 0x0B1A, 0x0B1E, - 0x0B22, 0x0B26, 0x0B2A, 0x0B2E, 0x0B32, 0x0B38, 0x0B3E, 0x0B44, - 0x0B4A, 0x0B50, 0x0B56, 0x0B5C, 0x0B62, 0x0B66, 0x0B6A, 0x0B6E, - 0x0B72, 0x0B76, 0x0B7A, 0x0B7E, 0x0B82, 0x0B88, 0x0B8E, 0x0B92, - 0x0B96, 0x0B9A, 0x0B9E, 0x0BA2, 0x0BA6, 0x0BAC, 0x0BB2, 0x0BB8, - 0x0BBE, 0x0BC4, 0x0BCA, 0x0BCE, 0x0BD2, 0x0BD6, 0x0BDA, 0x0BDE, - 0x0BE2, 0x0BE6, 0x0BEA, 0x0BEE, 0x0BF2, 0x0BF6, 0x0BFA, 0x0BFE, - 0x0C02, 0x0C08, 0x0C0E, 0x0C14, 0x0C1A, 0x0C1E, 0x0C22, 0x0C26, - // Block 24 - 0x0C2A, 0x0C2E, 0x0C32, 0x0C36, 0x0C3A, 0x0C3E, 0x0C42, 0x0C46, - 0x0C4A, 0x0C4E, 0x0C52, 0x0C56, 0x0C5A, 0x0C5E, 0x0C62, 0x0C66, - 0x0C6A, 0x0C6E, 0x0C72, 0x0C76, 0x0C7A, 0x0C7E, 0x0C82, 0x0C86, - 0x0C8A, 0x0C8E, 0x0000, 0x0C96, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0C9B, 0x0C9F, 0x0CA3, 0x0CA7, 0x0CAB, 0x0CB1, 0x0CB7, 0x0CBD, - 0x0CC3, 0x0CC9, 0x0CCF, 0x0CD5, 0x0CDB, 0x0CE1, 0x0CE7, 0x0CED, - 0x0CF3, 0x0CF9, 0x0CFF, 0x0D05, 0x0D0B, 0x0D11, 0x0D17, 0x0D1D, - 0x0D23, 0x0D27, 0x0D2B, 0x0D2F, 0x0D33, 0x0D37, 0x0D3B, 0x0D41, - // Block 25 - 0x0D47, 0x0D4D, 0x0D53, 0x0D59, 0x0D5F, 0x0D65, 0x0D6B, 0x0D71, - 0x0D77, 0x0D7B, 0x0D7F, 0x0D83, 0x0D87, 0x0D8B, 0x0D8F, 0x0D93, - 0x0D97, 0x0D9D, 0x0DA3, 0x0DA9, 0x0DAF, 0x0DB5, 0x0DBB, 0x0DC1, - 0x0DC7, 0x0DCD, 0x0DD3, 0x0DD9, 0x0DDF, 0x0DE5, 0x0DEB, 0x0DF1, - 0x0DF7, 0x0DFD, 0x0E03, 0x0E09, 0x0E0F, 0x0E13, 0x0E17, 0x0E1B, - 0x0E1F, 0x0E25, 0x0E2B, 0x0E31, 0x0E37, 0x0E3D, 0x0E43, 0x0E49, - 0x0E4F, 0x0E55, 0x0E5B, 0x0E5F, 0x0E63, 0x0E67, 0x0E6B, 0x0E6F, - 0x0E73, 0x0E77, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 26 - 0x0E7B, 0x0E80, 0x0E85, 0x0E8C, 0x0E93, 0x0E9A, 0x0EA1, 0x0EA8, - 0x0EAF, 0x0EB4, 0x0EB9, 0x0EC0, 0x0EC7, 0x0ECE, 0x0ED5, 0x0EDC, - 0x0EE3, 0x0EE8, 0x0EED, 0x0EF4, 0x0EFB, 0x0F02, 0x0000, 0x0000, - 0x0F09, 0x0F0E, 0x0F13, 0x0F1A, 0x0F21, 0x0F28, 0x0000, 0x0000, - 0x0F2F, 0x0F34, 0x0F39, 0x0F40, 0x0F47, 0x0F4E, 0x0F55, 0x0F5C, - 0x0F63, 0x0F68, 0x0F6D, 0x0F74, 0x0F7B, 0x0F82, 0x0F89, 0x0F90, - 0x0F97, 0x0F9C, 0x0FA1, 0x0FA8, 0x0FAF, 0x0FB6, 0x0FBD, 0x0FC4, - 0x0FCB, 0x0FD0, 0x0FD5, 0x0FDC, 0x0FE3, 0x0FEA, 0x0FF1, 0x0FF8, - // Block 27 - 0x0FFF, 0x1004, 0x1009, 0x1010, 0x1017, 0x101E, 0x0000, 0x0000, - 0x1025, 0x102A, 0x102F, 0x1036, 0x103D, 0x1044, 0x0000, 0x0000, - 0x104B, 0x1050, 0x1055, 0x105C, 0x1063, 0x106A, 0x1071, 0x1078, - 0x0000, 0x107F, 0x0000, 0x1084, 0x0000, 0x108B, 0x0000, 0x1092, - 0x1099, 0x109E, 0x10A3, 0x10AA, 0x10B1, 0x10B8, 0x10BF, 0x10C6, - 0x10CD, 0x10D2, 0x10D7, 0x10DE, 0x10E5, 0x10EC, 0x10F3, 0x10FA, - 0x1101, 0x0525, 0x1106, 0x052A, 0x110B, 0x052F, 0x1110, 0x0534, - 0x1115, 0x054A, 0x111A, 0x054F, 0x111F, 0x0554, 0x0000, 0x0000, - // Block 28 - 0x1124, 0x112B, 0x1132, 0x113B, 0x1144, 0x114D, 0x1156, 0x115F, - 0x1168, 0x116F, 0x1176, 0x117F, 0x1188, 0x1191, 0x119A, 0x11A3, - 0x11AC, 0x11B3, 0x11BA, 0x11C3, 0x11CC, 0x11D5, 0x11DE, 0x11E7, - 0x11F0, 0x11F7, 0x11FE, 0x1207, 0x1210, 0x1219, 0x1222, 0x122B, - 0x1234, 0x123B, 0x1242, 0x124B, 0x1254, 0x125D, 0x1266, 0x126F, - 0x1278, 0x127F, 0x1286, 0x128F, 0x1298, 0x12A1, 0x12AA, 0x12B3, - 0x12BC, 0x12C1, 0x12C6, 0x12CD, 0x12D2, 0x0000, 0x12D9, 0x12DE, - 0x12E5, 0x12EA, 0x12EF, 0x04EE, 0x12F4, 0x0000, 0x12FD, 0x0000, - // Block 29 - 0x0000, 0x1304, 0x130F, 0x1316, 0x131B, 0x0000, 0x1322, 0x1327, - 0x132E, 0x04F6, 0x1333, 0x04FB, 0x1338, 0x133D, 0x1349, 0x1355, - 0x1361, 0x1366, 0x136B, 0x0514, 0x0000, 0x0000, 0x1372, 0x1377, - 0x137E, 0x1383, 0x1388, 0x0500, 0x0000, 0x138D, 0x1399, 0x13A5, - 0x13B1, 0x13B6, 0x13BB, 0x0539, 0x13C2, 0x13C7, 0x13CC, 0x13D1, - 0x13D8, 0x13DD, 0x13E2, 0x050A, 0x13E7, 0x13EC, 0x04E3, 0x13F7, - 0x0000, 0x0000, 0x13F9, 0x1400, 0x1405, 0x0000, 0x140C, 0x1411, - 0x1418, 0x0505, 0x141D, 0x050F, 0x1422, 0x1427, 0x0000, 0x0000, - // Block 2A - 0x142E, 0x1432, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x14D6, 0x0000, - 0x0000, 0x0000, 0x091C, 0x0046, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x159F, 0x15A5, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x15AB, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x15B1, 0x15B7, 0x15BD, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2E - 0x0000, 0x0000, 0x0000, 0x0000, 0x15C3, 0x0000, 0x0000, 0x0000, - 0x0000, 0x15C9, 0x0000, 0x0000, 0x15CF, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x15D5, 0x0000, 0x15DB, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2F - 0x0000, 0x1603, 0x0000, 0x0000, 0x1609, 0x0000, 0x0000, 0x160F, - 0x0000, 0x1615, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x161B, 0x0000, 0x161F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1625, 0x162B, 0x162F, - 0x1633, 0x1639, 0x0000, 0x0000, 0x163F, 0x1645, 0x0000, 0x0000, - 0x164B, 0x1651, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 30 - 0x1657, 0x165D, 0x0000, 0x0000, 0x1663, 0x1669, 0x0000, 0x0000, - 0x166F, 0x1675, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x167B, 0x1681, 0x1687, 0x168D, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 31 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1693, 0x1699, 0x169F, 0x16A5, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x16AB, 0x16B1, 0x16B7, 0x16BD, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 32 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x16C3, 0x16C7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 33 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1814, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 34 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1B8A, 0x0000, 0x1B91, 0x0000, - 0x1B98, 0x0000, 0x1B9F, 0x0000, 0x1BA6, 0x0000, 0x1BAD, 0x0000, - 0x1BB4, 0x0000, 0x1BBB, 0x0000, 0x1BC2, 0x0000, 0x1BC9, 0x0000, - 0x1BD0, 0x0000, 0x1BD7, 0x0000, 0x0000, 0x1BDE, 0x0000, 0x1BE5, - 0x0000, 0x1BEC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1BF3, 0x1BFA, 0x0000, 0x1C01, 0x1C08, 0x0000, 0x1C0F, 0x1C16, - 0x0000, 0x1C1D, 0x1C24, 0x0000, 0x1C2B, 0x1C32, 0x0000, 0x0000, - // Block 35 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1C39, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1C4A, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1C58, 0x0000, 0x1C5F, 0x0000, - 0x1C66, 0x0000, 0x1C6D, 0x0000, 0x1C74, 0x0000, 0x1C7B, 0x0000, - 0x1C82, 0x0000, 0x1C89, 0x0000, 0x1C90, 0x0000, 0x1C97, 0x0000, - // Block 36 - 0x1C9E, 0x0000, 0x1CA5, 0x0000, 0x0000, 0x1CAC, 0x0000, 0x1CB3, - 0x0000, 0x1CBA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1CC1, 0x1CC8, 0x0000, 0x1CCF, 0x1CD6, 0x0000, 0x1CDD, 0x1CE4, - 0x0000, 0x1CEB, 0x1CF2, 0x0000, 0x1CF9, 0x1D00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1D07, 0x0000, 0x0000, 0x1D0E, - 0x1D15, 0x1D1C, 0x1D23, 0x0000, 0x0000, 0x0000, 0x1D2A, 0x0000, - // Block 37 - 0x2A81, 0x2A85, 0x1A9E, 0x2A89, 0x2A8D, 0x2A91, 0x2A95, 0x1B76, - 0x1B76, 0x2A99, 0x1ABE, 0x2A9D, 0x2AA1, 0x2AA5, 0x2AA9, 0x2AAD, - 0x2AB1, 0x2AB5, 0x2AB9, 0x2ABD, 0x2AC1, 0x2AC5, 0x2AC9, 0x2ACD, - 0x2AD1, 0x2AD5, 0x2AD9, 0x2ADD, 0x2AE1, 0x2AE5, 0x2AE9, 0x2AED, - 0x2AF1, 0x2AF5, 0x2AF9, 0x2AFD, 0x2B01, 0x2B05, 0x2B09, 0x2B0D, - 0x2B11, 0x2B15, 0x2B19, 0x2B1D, 0x2B21, 0x2B25, 0x2B29, 0x2B2D, - 0x2B31, 0x2B35, 0x2B39, 0x2B3D, 0x1A16, 0x2B41, 0x2B45, 0x2B49, - 0x2B4D, 0x2B51, 0x2B55, 0x2B59, 0x2B5D, 0x2B61, 0x2B65, 0x2B69, - // Block 38 - 0x1B3A, 0x2B6D, 0x2B71, 0x2B75, 0x2B79, 0x2B7D, 0x2B81, 0x2B85, - 0x2B89, 0x2B8D, 0x2B91, 0x2B95, 0x2B99, 0x2B9D, 0x2BA1, 0x2BA5, - 0x2BA9, 0x2BAD, 0x2BB1, 0x2BB5, 0x2BB9, 0x2BBD, 0x2BC1, 0x2BC5, - 0x2BC9, 0x2BCD, 0x2BD1, 0x2BD5, 0x2AC1, 0x2BD9, 0x2BDD, 0x2BE1, - 0x2BE5, 0x2BE9, 0x2BED, 0x2BF1, 0x2BF5, 0x2BF9, 0x2BFD, 0x2C01, - 0x2C05, 0x2C09, 0x2C0D, 0x2C11, 0x2C15, 0x2C19, 0x2C1D, 0x2C21, - 0x2C25, 0x1AA6, 0x2C29, 0x2C2D, 0x2C31, 0x2C35, 0x2C39, 0x2C3D, - 0x2C41, 0x2C45, 0x2C49, 0x2C4D, 0x2C51, 0x2C55, 0x2C59, 0x2C5D, - // Block 39 - 0x2C61, 0x18BA, 0x2C65, 0x2C69, 0x2C6D, 0x2C71, 0x2C75, 0x2C79, - 0x2C7D, 0x2C81, 0x186E, 0x2C85, 0x2C89, 0x2C8D, 0x2C91, 0x2C95, - 0x2C99, 0x2C9D, 0x2CA1, 0x2CA5, 0x2CA9, 0x2CAD, 0x2CB1, 0x2CB5, - 0x2CB9, 0x2CBD, 0x2CC1, 0x2CC5, 0x2CC9, 0x2CCD, 0x2CD1, 0x2CD5, - 0x2CD9, 0x2C21, 0x2CDD, 0x2CE1, 0x2CE5, 0x2CE9, 0x2CED, 0x2CF1, - 0x2CF5, 0x2CF9, 0x2BE1, 0x2CFD, 0x2D01, 0x2D05, 0x2D09, 0x2D0D, - 0x2D11, 0x2D15, 0x2D19, 0x2D1D, 0x2D21, 0x2D25, 0x2D29, 0x2D2D, - 0x2D31, 0x2D35, 0x2D39, 0x2D3D, 0x2D41, 0x2D45, 0x2D49, 0x2AC1, - // Block 3A - 0x2D4D, 0x2D51, 0x2D55, 0x2D59, 0x1B72, 0x2D5D, 0x2D61, 0x2D65, - 0x2D69, 0x2D6D, 0x2D71, 0x2D75, 0x2D79, 0x2D7D, 0x2D81, 0x2D85, - 0x2D89, 0x2173, 0x2D8D, 0x2D91, 0x2D95, 0x2D99, 0x2D9D, 0x2DA1, - 0x2DA5, 0x2DA9, 0x2DAD, 0x2BE9, 0x2DB1, 0x2DB5, 0x2DB9, 0x2DBD, - 0x2DC1, 0x2DC5, 0x2DC9, 0x2DCD, 0x2DD1, 0x2DD5, 0x2DD9, 0x2DDD, - 0x2DE1, 0x1ABA, 0x2DE5, 0x2DE9, 0x2DED, 0x2DF1, 0x2DF5, 0x2DF9, - 0x2DFD, 0x2E01, 0x2E05, 0x2E09, 0x2E0D, 0x2E11, 0x2E15, 0x19F6, - 0x2E19, 0x2E1D, 0x2E21, 0x2E25, 0x2E29, 0x2E2D, 0x2E31, 0x2E35, - // Block 3B - 0x2E39, 0x2E3D, 0x2E41, 0x2E45, 0x2E49, 0x2E4D, 0x2E51, 0x2E55, - 0x1A62, 0x2E59, 0x1A6E, 0x2E5D, 0x2E61, 0x2E65, 0x0000, 0x0000, - 0x2E69, 0x0000, 0x2E6D, 0x0000, 0x0000, 0x2E71, 0x2E75, 0x2E79, - 0x2E7D, 0x2E81, 0x2E85, 0x2E89, 0x2E8D, 0x2E91, 0x1A12, 0x0000, - 0x2E95, 0x0000, 0x2E99, 0x0000, 0x0000, 0x2E9D, 0x2EA1, 0x0000, - 0x0000, 0x0000, 0x2EA5, 0x2EA9, 0x2EAD, 0x2EB1, 0x0000, 0x0000, - 0x2EB5, 0x2EB9, 0x2EBD, 0x2EC1, 0x2EC5, 0x2EC9, 0x2ECD, 0x2ED1, - 0x2ED5, 0x2ED9, 0x2EDD, 0x2EE1, 0x18D6, 0x2EE5, 0x2EE9, 0x2EED, - // Block 3C - 0x2EF1, 0x2EF5, 0x2EF9, 0x2EFD, 0x2F01, 0x2F05, 0x2F09, 0x2F0D, - 0x2F11, 0x2F15, 0x2F19, 0x2F1D, 0x2187, 0x2F21, 0x2F25, 0x2F29, - 0x2F2D, 0x2197, 0x2F31, 0x2F35, 0x2F39, 0x2F3D, 0x2F41, 0x2CB1, - 0x2F45, 0x2F49, 0x2F4D, 0x2F51, 0x2F55, 0x2F59, 0x2F59, 0x2F5D, - 0x2F61, 0x2F65, 0x2F69, 0x2F6D, 0x2F71, 0x2F75, 0x2F79, 0x2E9D, - 0x2F7D, 0x2F81, 0x2F85, 0x2F89, 0x2F8D, 0x2F92, 0x0000, 0x0000, - 0x2F96, 0x2F9A, 0x2F9E, 0x2FA2, 0x2FA6, 0x2FAA, 0x2FAE, 0x2FB2, - 0x2ECD, 0x2FB6, 0x2FBA, 0x2FBE, 0x2E69, 0x2FC2, 0x2FC6, 0x2FCA, - // Block 3D - 0x2FCE, 0x2FD2, 0x2FD6, 0x2FDA, 0x2FDE, 0x2FE2, 0x2FE6, 0x2FEA, - 0x2FEE, 0x2EED, 0x2FF2, 0x2EF1, 0x2FF6, 0x2FFA, 0x2FFE, 0x3002, - 0x3006, 0x2E6D, 0x2B15, 0x300A, 0x300E, 0x195A, 0x2C25, 0x2D71, - 0x3012, 0x3016, 0x2F0D, 0x301A, 0x2F11, 0x301E, 0x3022, 0x3026, - 0x2E75, 0x302A, 0x302E, 0x3032, 0x3036, 0x303A, 0x2E79, 0x303E, - 0x3042, 0x3046, 0x304A, 0x304E, 0x3052, 0x2F41, 0x3056, 0x305A, - 0x2CB1, 0x305E, 0x2F51, 0x3062, 0x3066, 0x306A, 0x306E, 0x3072, - 0x2F65, 0x3076, 0x2E99, 0x307A, 0x2F69, 0x2BD9, 0x307E, 0x2F6D, - // Block 3E - 0x3082, 0x2F75, 0x3086, 0x308A, 0x308E, 0x3092, 0x3096, 0x2F7D, - 0x2E8D, 0x309A, 0x2F81, 0x309E, 0x2F85, 0x30A2, 0x1B76, 0x30A6, - 0x30AB, 0x30B0, 0x30B5, 0x30B9, 0x30BD, 0x30C1, 0x30C6, 0x30CB, - 0x30D0, 0x30D4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3F - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3105, 0x0000, 0x310A, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x3124, 0x3129, 0x312E, 0x3135, 0x313C, 0x3141, - 0x3146, 0x314B, 0x3150, 0x3155, 0x315A, 0x315F, 0x3164, 0x0000, - 0x3169, 0x316E, 0x3173, 0x3178, 0x317D, 0x0000, 0x3182, 0x0000, - // Block 40 - 0x3187, 0x318C, 0x0000, 0x3191, 0x3196, 0x0000, 0x319B, 0x31A0, - 0x31A5, 0x31AA, 0x31AF, 0x31B4, 0x31B9, 0x31BE, 0x31C3, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 41 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x3A73, 0x0000, 0x3A7C, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3A85, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 42 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3A8E, 0x3A97, - 0x3AA0, 0x3AAD, 0x3ABA, 0x3AC7, 0x3AD4, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 43 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3AE1, 0x3AEA, 0x3AF3, 0x3B00, 0x3B0D, - // Block 44 - 0x3B1A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 45 - 0x3D23, 0x3D27, 0x3D2B, 0x3D2F, 0x3D34, 0x2EB5, 0x3D38, 0x3D3C, - 0x3D40, 0x3D44, 0x2EB9, 0x3D48, 0x3D4C, 0x3D50, 0x2EBD, 0x3D55, - 0x3D59, 0x3D5D, 0x3D61, 0x3D66, 0x3D6A, 0x3C71, 0x3D6E, 0x3D73, - 0x3D77, 0x3D7B, 0x3D7F, 0x2F9A, 0x3D83, 0x1866, 0x3D88, 0x3D8C, - 0x3D90, 0x3D94, 0x3CB9, 0x3D98, 0x3D9C, 0x2FAE, 0x2EC1, 0x2EC5, - 0x2FB2, 0x3DA0, 0x3DA4, 0x2BF1, 0x3DA8, 0x2EC9, 0x3DAC, 0x3DB0, - 0x3DB4, 0x3DB8, 0x3DB8, 0x3DB8, 0x3DBC, 0x3DC1, 0x3DC5, 0x3DC9, - 0x3DCD, 0x3DD2, 0x3DD6, 0x3DDA, 0x3DDE, 0x3DE2, 0x3DE6, 0x3DEA, - // Block 46 - 0x3DEE, 0x3DF2, 0x3DF6, 0x3DFA, 0x3DFE, 0x3E02, 0x3E02, 0x2FBA, - 0x3E06, 0x3E0A, 0x3E0E, 0x3E12, 0x2ED1, 0x3E16, 0x3E1A, 0x3E1E, - 0x2E39, 0x3E22, 0x3E26, 0x3E2A, 0x3E2E, 0x3E32, 0x3E36, 0x3E3A, - 0x3E3E, 0x3E42, 0x3E47, 0x3E4B, 0x3E4F, 0x3C55, 0x3E53, 0x3E57, - 0x3E5B, 0x3E60, 0x3E65, 0x3E69, 0x3E6D, 0x3E71, 0x3E75, 0x3E79, - 0x3E7D, 0x3E81, 0x3E85, 0x3E85, 0x3E89, 0x3E8E, 0x3E92, 0x2BE1, - 0x3E96, 0x3E9A, 0x3E9F, 0x3EA3, 0x3EA7, 0x18CE, 0x3EAB, 0x3EAF, - 0x18D6, 0x3EB3, 0x3EB7, 0x3EBB, 0x3EC0, 0x3EC4, 0x3EC9, 0x3ECD, - // Block 47 - 0x3ED1, 0x3ED5, 0x3ED9, 0x3EDD, 0x3EE1, 0x3EE5, 0x3EE9, 0x3EED, - 0x3EF1, 0x3EF5, 0x3EFA, 0x3EFE, 0x3F02, 0x3F06, 0x2B11, 0x3F0A, - 0x18FE, 0x3F0F, 0x3F0F, 0x3F14, 0x3F18, 0x3F18, 0x3F1C, 0x3F20, - 0x3F25, 0x3F2A, 0x3F2E, 0x3F32, 0x3F36, 0x3F3A, 0x3F3E, 0x3F42, - 0x3F46, 0x3F4A, 0x3F4E, 0x2EE5, 0x3F52, 0x3F57, 0x3F5B, 0x3F5F, - 0x2FEA, 0x3F5F, 0x3F63, 0x2EED, 0x3F67, 0x3F6B, 0x3F6F, 0x3F73, - 0x2EF1, 0x2AA5, 0x3F77, 0x3F7B, 0x3F7F, 0x3F83, 0x3F87, 0x3F8B, - 0x3F8F, 0x3F94, 0x3F98, 0x3F9C, 0x3FA0, 0x3FA4, 0x3FA8, 0x3FAD, - // Block 48 - 0x3FB1, 0x3FB5, 0x3FB9, 0x3FBD, 0x3FC1, 0x3FC5, 0x3FC9, 0x3FCD, - 0x2EF5, 0x3FD1, 0x3FD5, 0x3FDA, 0x3FDE, 0x3FE2, 0x3FE6, 0x2EFD, - 0x3FEA, 0x3FEE, 0x3FF2, 0x3FF6, 0x3FFA, 0x3FFE, 0x4002, 0x4006, - 0x2B15, 0x300A, 0x400A, 0x400E, 0x4012, 0x4016, 0x401B, 0x401F, - 0x4023, 0x4027, 0x2F01, 0x402B, 0x4030, 0x4034, 0x4038, 0x30B5, - 0x403C, 0x4040, 0x4044, 0x4048, 0x404C, 0x4051, 0x4055, 0x4059, - 0x405D, 0x4062, 0x4066, 0x406A, 0x406E, 0x2C25, 0x4072, 0x4076, - 0x407B, 0x4080, 0x4085, 0x4089, 0x408E, 0x4092, 0x4096, 0x409A, - // Block 49 - 0x409E, 0x2F05, 0x2D71, 0x40A2, 0x40A6, 0x40AA, 0x40AE, 0x40B3, - 0x40B7, 0x40BB, 0x40BF, 0x3016, 0x40C3, 0x40C7, 0x40CC, 0x40D0, - 0x40D4, 0x40D9, 0x40DE, 0x40E2, 0x301A, 0x40E6, 0x40EA, 0x40EE, - 0x40F2, 0x40F6, 0x40FA, 0x40FE, 0x4103, 0x4107, 0x410C, 0x4110, - 0x4115, 0x3022, 0x4119, 0x411D, 0x4122, 0x4126, 0x412A, 0x412F, - 0x4134, 0x4138, 0x413C, 0x4140, 0x4144, 0x4144, 0x4148, 0x414C, - 0x302A, 0x4150, 0x4154, 0x4158, 0x415C, 0x4160, 0x4165, 0x4169, - 0x2BED, 0x416E, 0x4173, 0x4177, 0x417C, 0x4181, 0x4186, 0x418A, - // Block 4A - 0x3042, 0x418E, 0x4193, 0x4198, 0x419D, 0x41A2, 0x41A6, 0x41A6, - 0x3046, 0x30BD, 0x41AA, 0x41AE, 0x41B2, 0x41B6, 0x41BB, 0x2B59, - 0x304E, 0x41BF, 0x41C3, 0x2F2D, 0x41C8, 0x41CD, 0x2E89, 0x41D2, - 0x41D6, 0x2F39, 0x41DA, 0x41DE, 0x41E2, 0x41E7, 0x41E7, 0x41EC, - 0x41F0, 0x41F4, 0x41F9, 0x41FD, 0x4201, 0x4205, 0x420A, 0x420E, - 0x4212, 0x4216, 0x421A, 0x421E, 0x4223, 0x4227, 0x422B, 0x422F, - 0x4233, 0x4237, 0x423B, 0x4240, 0x4245, 0x4249, 0x424E, 0x4252, - 0x4257, 0x425B, 0x2F51, 0x425F, 0x4264, 0x4269, 0x426D, 0x4272, - // Block 4B - 0x4276, 0x427B, 0x427F, 0x4283, 0x4287, 0x428B, 0x428F, 0x4293, - 0x4298, 0x429D, 0x42A2, 0x3F14, 0x42A7, 0x42AB, 0x42AF, 0x42B3, - 0x42B7, 0x42BB, 0x42BF, 0x42C3, 0x42C7, 0x42CB, 0x42CF, 0x42D3, - 0x2C31, 0x42D8, 0x42DC, 0x42E0, 0x42E4, 0x42E8, 0x42EC, 0x2F5D, - 0x42F0, 0x42F4, 0x42F8, 0x42FC, 0x4300, 0x4305, 0x430A, 0x430F, - 0x4313, 0x4317, 0x431B, 0x431F, 0x4324, 0x4328, 0x432D, 0x4331, - 0x4335, 0x433A, 0x433F, 0x4343, 0x2B45, 0x4347, 0x434B, 0x434F, - 0x4353, 0x4357, 0x435B, 0x306A, 0x435F, 0x4363, 0x4367, 0x436B, - // Block 4C - 0x436F, 0x4373, 0x4377, 0x437B, 0x1A66, 0x437F, 0x4384, 0x4388, - 0x438C, 0x4390, 0x4394, 0x4398, 0x439D, 0x43A2, 0x43A6, 0x43AA, - 0x307E, 0x3082, 0x1A82, 0x43AE, 0x43B3, 0x43B7, 0x43BB, 0x43BF, - 0x43C3, 0x43C8, 0x43CD, 0x43D1, 0x43D5, 0x43D9, 0x43DE, 0x3086, - 0x43E2, 0x43E7, 0x43EC, 0x43F0, 0x43F4, 0x43F8, 0x43FD, 0x4401, - 0x4405, 0x4409, 0x440D, 0x4411, 0x4415, 0x4419, 0x441E, 0x4422, - 0x4426, 0x442A, 0x442F, 0x4433, 0x4437, 0x443B, 0x443F, 0x4444, - 0x4449, 0x444D, 0x4451, 0x4455, 0x445A, 0x445E, 0x309E, 0x309E, - // Block 4D - 0x4463, 0x4467, 0x446C, 0x4470, 0x4474, 0x4478, 0x447C, 0x4480, - 0x4484, 0x4488, 0x30A2, 0x448D, 0x4491, 0x4495, 0x4499, 0x449D, - 0x44A1, 0x44A6, 0x44AA, 0x44AF, 0x44B4, 0x1B42, 0x44B9, 0x1B52, - 0x44BD, 0x44C1, 0x44C5, 0x44C9, 0x1B66, 0x44CD, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +var nfcDecompValues = [4992]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, + // 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, + // 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, + // Block 0x6, offset 0x180 + 0x01a0: 0x02ca, 0x01a1: 0x02ce, + 0x01af: 0x02d2, + 0x01b0: 0x02d6, + // 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, + // 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, + // Block 0x9, offset 0x240 + 0x0240: 0x04cc, 0x0241: 0x04cf, 0x0243: 0x04d2, 0x0244: 0x04d5, + 0x0274: 0x04da, + 0x027e: 0x04e1, + // 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, + // Block 0xb, offset 0x2c0 + 0x02ca: 0x0540, 0x02cb: 0x0545, + 0x02cc: 0x054a, 0x02cd: 0x054f, 0x02ce: 0x0554, + 0x02d3: 0x0562, 0x02d4: 0x0567, + // Block 0xc, offset 0x300 + 0x0300: 0x0584, 0x0301: 0x0589, 0x0303: 0x058e, + 0x0307: 0x0593, + 0x030c: 0x0598, 0x030d: 0x059d, 0x030e: 0x05a2, + 0x0319: 0x05a7, + 0x0339: 0x05ac, + // Block 0xd, offset 0x340 + 0x0350: 0x05b1, 0x0351: 0x05b6, + 0x0353: 0x05bb, 0x0357: 0x05c0, + 0x035c: 0x05c5, 0x035d: 0x05ca, + 0x035e: 0x05cf, + 0x0376: 0x05d4, 0x0377: 0x05d9, + // 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, + // Block 0xf, offset 0x3c0 + 0x03e2: 0x068d, 0x03e3: 0x0692, + 0x03e4: 0x0697, 0x03e5: 0x069c, 0x03e6: 0x06a1, + // Block 0x10, offset 0x400 + 0x0400: 0x06ba, 0x0402: 0x06bf, + 0x0413: 0x06c4, + // Block 0x11, offset 0x440 + 0x0469: 0x06c9, + 0x0471: 0x06d0, 0x0474: 0x06d7, + // Block 0x12, offset 0x480 + 0x0498: 0x06de, 0x0499: 0x06e5, 0x049a: 0x06ec, 0x049b: 0x06f3, 0x049c: 0x06fa, 0x049d: 0x0701, + 0x049e: 0x0708, 0x049f: 0x070f, + // Block 0x13, offset 0x4c0 + 0x04cb: 0x0716, + 0x04cc: 0x071d, + 0x04dc: 0x0724, 0x04dd: 0x072b, + 0x04df: 0x0732, + // Block 0x14, offset 0x500 + 0x0533: 0x0739, + 0x0536: 0x0740, + // 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, } // nfcDecompLookup: 832 bytes // Block 0 is the null block. -var nfcDecompLookup = [...]uint8{ - // Block 0 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 1 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 2 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 3 - 0x00, 0x00, 0x00, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0A, 0x0B, - 0x0C, 0x0D, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 4 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x12, 0x00, 0x13, - 0x14, 0x15, 0x00, 0x00, 0x00, 0x16, 0x17, 0x18, - 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x1E, 0x00, - // Block 5 - 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x21, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, - // Block 6 - 0x2A, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x2C, 0x2D, - 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 7 - 0x00, 0x34, 0x35, 0x36, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 8 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x3A, - 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 9 - 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block A - 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x43, 0x44, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block B - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, - 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block C - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +var nfcDecompLookup = [832]uint8{ + // Block 0x0, offset 0x0 + // 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, + 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, + // 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, + // Block 0x6, offset 0x180 + 0x180: 0x2a, 0x184: 0x2b, 0x186: 0x2c, 0x187: 0x2d, + 0x188: 0x2e, 0x189: 0x2f, 0x18a: 0x30, 0x18b: 0x31, 0x18c: 0x32, + 0x1ab: 0x33, + // Block 0x7, offset 0x1c0 + 0x1c1: 0x34, 0x1c2: 0x35, 0x1c3: 0x36, + // Block 0x8, offset 0x200 + 0x224: 0x37, 0x225: 0x38, 0x226: 0x39, 0x227: 0x3a, + 0x228: 0x3b, 0x229: 0x3c, 0x22a: 0x3d, 0x22b: 0x3e, 0x22c: 0x3f, 0x22d: 0x40, + // Block 0x9, offset 0x240 + 0x242: 0x41, + // Block 0xa, offset 0x280 + 0x285: 0x42, 0x286: 0x43, 0x287: 0x44, + // Block 0xb, offset 0x2c0 + 0x2e0: 0x45, 0x2e1: 0x46, 0x2e2: 0x47, 0x2e3: 0x48, 0x2e4: 0x49, 0x2e5: 0x4a, 0x2e6: 0x4b, 0x2e7: 0x4c, + 0x2e8: 0x4d, + // Block 0xc, offset 0x300 + 0x311: 0x09, + 0x31d: 0x0a, + 0x32f: 0x0b, } +var nfcDecompTrie = trie{nfcDecompLookup[:], nfcDecompValues[:]} + // nfkcDecompValues: 10176 entries, 20352 bytes // Block 2 is the null block. -var nfkcDecompValues = [...]uint16{ - // Block 0 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, - 0x0000, 0x0000, 0x000D, 0x000F, 0x0011, 0x0015, 0x0000, 0x0000, - 0x0018, 0x001C, 0x001E, 0x0000, 0x0020, 0x0026, 0x002C, 0x0000, - // Block 4 - 0x0032, 0x0036, 0x003A, 0x003E, 0x0042, 0x0046, 0x0000, 0x004A, - 0x004E, 0x0052, 0x0056, 0x005A, 0x005E, 0x0062, 0x0066, 0x006A, - 0x0000, 0x006E, 0x0072, 0x0076, 0x007A, 0x007E, 0x0082, 0x0000, - 0x0000, 0x0086, 0x008A, 0x008E, 0x0092, 0x0096, 0x0000, 0x0000, - 0x009A, 0x009E, 0x00A2, 0x00A6, 0x00AA, 0x00AE, 0x0000, 0x00B2, - 0x00B6, 0x00BA, 0x00BE, 0x00C2, 0x00C6, 0x00CA, 0x00CE, 0x00D2, - 0x0000, 0x00D6, 0x00DA, 0x00DE, 0x00E2, 0x00E6, 0x00EA, 0x0000, - 0x0000, 0x00EE, 0x00F2, 0x00F6, 0x00FA, 0x00FE, 0x0000, 0x0102, - // Block 5 - 0x0106, 0x010A, 0x010E, 0x0112, 0x0116, 0x011A, 0x011E, 0x0122, - 0x0126, 0x012A, 0x012E, 0x0132, 0x0136, 0x013A, 0x013E, 0x0142, - 0x0000, 0x0000, 0x0146, 0x014A, 0x014E, 0x0152, 0x0156, 0x015A, - 0x015E, 0x0162, 0x0166, 0x016A, 0x016E, 0x0172, 0x0176, 0x017A, - 0x017E, 0x0182, 0x0186, 0x018A, 0x018E, 0x0192, 0x0000, 0x0000, - 0x0196, 0x019A, 0x019E, 0x01A2, 0x01A6, 0x01AA, 0x01AE, 0x01B2, - 0x01B6, 0x0000, 0x01BA, 0x01BD, 0x01C0, 0x01C4, 0x01C8, 0x01CC, - 0x0000, 0x01D0, 0x01D4, 0x01D8, 0x01DC, 0x01E0, 0x01E4, 0x01E8, - // Block 6 - 0x01EC, 0x0000, 0x0000, 0x01F0, 0x01F4, 0x01F8, 0x01FC, 0x0200, - 0x0204, 0x0208, 0x0000, 0x0000, 0x020C, 0x0210, 0x0214, 0x0218, - 0x021C, 0x0220, 0x0000, 0x0000, 0x0224, 0x0228, 0x022C, 0x0230, - 0x0234, 0x0238, 0x023C, 0x0240, 0x0244, 0x0248, 0x024C, 0x0250, - 0x0254, 0x0258, 0x025C, 0x0260, 0x0264, 0x0268, 0x0000, 0x0000, - 0x026C, 0x0270, 0x0274, 0x0278, 0x027C, 0x0280, 0x0284, 0x0288, - 0x028C, 0x0290, 0x0294, 0x0298, 0x029C, 0x02A0, 0x02A4, 0x02A8, - 0x02AC, 0x02B0, 0x02B4, 0x02B8, 0x02BC, 0x02C0, 0x02C4, 0x02C8, - // Block 7 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x02CA, 0x02CE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x02D2, - 0x02D6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 8 - 0x0000, 0x0000, 0x0000, 0x0000, 0x02DA, 0x02DF, 0x02E4, 0x02E9, - 0x02EC, 0x02EF, 0x02F2, 0x02F5, 0x02F8, 0x02FB, 0x02FF, 0x0303, - 0x0307, 0x030B, 0x030F, 0x0313, 0x0317, 0x031B, 0x0321, 0x0327, - 0x032D, 0x0333, 0x0339, 0x033F, 0x0345, 0x0000, 0x034B, 0x0351, - 0x0357, 0x035D, 0x0363, 0x0368, 0x0000, 0x0000, 0x036D, 0x0371, - 0x0375, 0x0379, 0x037D, 0x0381, 0x0385, 0x038B, 0x0391, 0x0396, - 0x039B, 0x039F, 0x03A2, 0x03A5, 0x03A8, 0x03AC, 0x0000, 0x0000, - 0x03B0, 0x03B4, 0x03B8, 0x03BE, 0x03C4, 0x03C9, 0x03CE, 0x03D3, - // Block 9 - 0x03D8, 0x03DC, 0x03E0, 0x03E4, 0x03E8, 0x03EC, 0x03F0, 0x03F4, - 0x03F8, 0x03FC, 0x0400, 0x0404, 0x0408, 0x040C, 0x0410, 0x0414, - 0x0418, 0x041C, 0x0420, 0x0424, 0x0428, 0x042C, 0x0430, 0x0434, - 0x0438, 0x043C, 0x0440, 0x0444, 0x0000, 0x0000, 0x0448, 0x044C, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0450, 0x0454, - 0x0458, 0x045C, 0x0460, 0x0466, 0x046C, 0x0472, 0x0478, 0x047C, - 0x0480, 0x0486, 0x048C, 0x0490, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0494, 0x0496, 0x0499, 0x049B, 0x049D, 0x04A0, 0x04A3, 0x04A6, - 0x04A8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x04AA, 0x04AE, 0x04B2, 0x04B6, 0x04BA, 0x04BE, 0x0000, 0x0000, - 0x04C2, 0x04C5, 0x02C8, 0x04C7, 0x04C9, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block C - 0x04CC, 0x04CF, 0x0000, 0x04D2, 0x04D5, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x04DA, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x04DD, 0x0000, 0x0000, 0x0000, 0x04E1, 0x0000, - // Block D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0011, 0x04E8, 0x04EE, 0x04F3, - 0x04F6, 0x04FB, 0x0500, 0x0000, 0x0505, 0x0000, 0x050A, 0x050F, - 0x0514, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x051B, 0x0520, 0x0525, 0x052A, 0x052F, 0x0534, - 0x0539, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block E - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0540, 0x0545, 0x054A, 0x054F, 0x0554, 0x0000, - 0x0559, 0x055C, 0x055F, 0x050A, 0x0520, 0x056C, 0x056F, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0572, 0x0575, 0x0578, 0x0000, 0x057B, 0x057E, 0x0000, 0x0000, - 0x0000, 0x0581, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block F - 0x0584, 0x0589, 0x0000, 0x058E, 0x0000, 0x0000, 0x0000, 0x0593, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0598, 0x059D, 0x05A2, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x05A7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x05AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 10 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x05B1, 0x05B6, 0x0000, 0x05BB, 0x0000, 0x0000, 0x0000, 0x05C0, - 0x0000, 0x0000, 0x0000, 0x0000, 0x05C5, 0x05CA, 0x05CF, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x05D4, 0x05D9, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 11 - 0x0000, 0x05DE, 0x05E3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x05E8, 0x05ED, 0x05F2, 0x05F7, 0x0000, 0x0000, 0x05FC, 0x0601, - 0x0000, 0x0000, 0x0606, 0x060B, 0x0610, 0x0615, 0x061A, 0x061F, - 0x0000, 0x0000, 0x0624, 0x0629, 0x062E, 0x0633, 0x0638, 0x063D, - 0x0000, 0x0000, 0x0642, 0x0647, 0x064C, 0x0651, 0x0656, 0x065B, - 0x0660, 0x0665, 0x066A, 0x066F, 0x0674, 0x0679, 0x0000, 0x0000, - 0x067E, 0x0683, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 12 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0688, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 13 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x068D, 0x0692, 0x0697, 0x069C, 0x06A1, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 14 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x06A6, 0x06AB, 0x06B0, - 0x06B5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 15 - 0x06BA, 0x0000, 0x06BF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x06C4, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 16 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x06C9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x06D0, 0x0000, 0x0000, 0x06D7, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 17 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x06DE, 0x06E5, 0x06EC, 0x06F3, 0x06FA, 0x0701, 0x0708, 0x070F, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 18 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0716, 0x071D, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0724, 0x072B, 0x0000, 0x0732, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 19 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0739, 0x0000, 0x0000, 0x0740, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0747, 0x074E, 0x0755, 0x0000, 0x0000, 0x075C, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0763, 0x0000, 0x0000, 0x076A, 0x0771, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0778, 0x077F, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0786, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x078D, 0x0794, 0x079B, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1E - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x07A2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1F - 0x07A9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x07B0, - 0x07B7, 0x0000, 0x07BE, 0x07C5, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 20 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x07CF, 0x07D6, 0x07DD, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 21 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x07E4, 0x0000, 0x07EB, 0x07F2, 0x07FC, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 22 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0803, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 23 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x080A, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 24 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0811, 0x0818, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 25 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x081F, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 26 - 0x0000, 0x0000, 0x0000, 0x0823, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x082A, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0831, 0x0000, 0x0000, 0x0000, 0x0000, 0x0838, - 0x0000, 0x0000, 0x0000, 0x0000, 0x083F, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0846, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x084D, 0x0000, 0x0854, 0x085B, 0x0862, - 0x086C, 0x0873, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 27 - 0x0000, 0x087D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0884, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x088B, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0892, 0x0000, 0x0000, 0x0000, 0x0000, 0x0899, - 0x0000, 0x0000, 0x0000, 0x0000, 0x08A0, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x08A7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 28 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x08AE, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 29 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x08B5, 0x0000, 0x0000, 0x0000, - // Block 2A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x08B9, 0x0000, - 0x08C0, 0x0000, 0x08C7, 0x0000, 0x08CE, 0x0000, 0x08D5, 0x0000, - 0x0000, 0x0000, 0x08DC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x08E3, 0x0000, 0x08EA, 0x0000, 0x0000, - // Block 2B - 0x08F1, 0x08F8, 0x0000, 0x08FF, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0906, 0x0908, 0x090B, 0x0000, - 0x090D, 0x090F, 0x0911, 0x0914, 0x0916, 0x0918, 0x091A, 0x091C, - 0x091E, 0x0920, 0x0922, 0x0000, 0x0924, 0x0926, 0x0929, 0x092B, - // Block 2D - 0x092D, 0x092F, 0x0931, 0x0007, 0x0933, 0x0936, 0x0939, 0x093D, - 0x093F, 0x0941, 0x0943, 0x0946, 0x0949, 0x094C, 0x0000, 0x094E, - 0x0950, 0x0952, 0x001E, 0x0955, 0x0958, 0x095C, 0x0960, 0x0962, - 0x0964, 0x0966, 0x096A, 0x096D, 0x096F, 0x0559, 0x0973, 0x0976, - 0x056C, 0x0979, 0x097C, 0x049B, 0x0964, 0x096D, 0x0559, 0x0973, - 0x0575, 0x056C, 0x0979, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x097E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2E - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0981, 0x0984, 0x0986, 0x0989, 0x0949, - 0x098C, 0x098E, 0x0991, 0x0994, 0x0997, 0x099A, 0x099D, 0x09A0, - 0x09A4, 0x09A7, 0x09AA, 0x09AE, 0x09B1, 0x09B4, 0x09B7, 0x09BA, - 0x09BD, 0x09C0, 0x09C3, 0x09C6, 0x09C9, 0x09CC, 0x09CF, 0x09D2, - 0x09D5, 0x09D9, 0x09DC, 0x09DF, 0x09E1, 0x09E4, 0x09E7, 0x055C, - // Block 2F - 0x09EA, 0x09EE, 0x09F2, 0x09F6, 0x09FA, 0x09FE, 0x0A02, 0x0A06, - 0x0A0A, 0x0A10, 0x0A16, 0x0A1A, 0x0A1E, 0x0A22, 0x0A26, 0x0A2A, - 0x0A2E, 0x0A32, 0x0A36, 0x0A3A, 0x0A3E, 0x0A44, 0x0A4A, 0x0A50, - 0x0A56, 0x0A5A, 0x0A5E, 0x0A62, 0x0A66, 0x0A6C, 0x0A72, 0x0A76, - 0x0A7A, 0x0A7E, 0x0A82, 0x0A86, 0x0A8A, 0x0A8E, 0x0A92, 0x0A96, - 0x0A9A, 0x0A9E, 0x0AA2, 0x0AA6, 0x0AAA, 0x0AAE, 0x0AB2, 0x0AB8, - 0x0ABE, 0x0AC2, 0x0AC6, 0x0ACA, 0x0ACE, 0x0AD2, 0x0AD6, 0x0ADA, - 0x0ADE, 0x0AE4, 0x0AEA, 0x0AEE, 0x0AF2, 0x0AF6, 0x0AFA, 0x0AFE, - // Block 30 - 0x0B02, 0x0B06, 0x0B0A, 0x0B0E, 0x0B12, 0x0B16, 0x0B1A, 0x0B1E, - 0x0B22, 0x0B26, 0x0B2A, 0x0B2E, 0x0B32, 0x0B38, 0x0B3E, 0x0B44, - 0x0B4A, 0x0B50, 0x0B56, 0x0B5C, 0x0B62, 0x0B66, 0x0B6A, 0x0B6E, - 0x0B72, 0x0B76, 0x0B7A, 0x0B7E, 0x0B82, 0x0B88, 0x0B8E, 0x0B92, - 0x0B96, 0x0B9A, 0x0B9E, 0x0BA2, 0x0BA6, 0x0BAC, 0x0BB2, 0x0BB8, - 0x0BBE, 0x0BC4, 0x0BCA, 0x0BCE, 0x0BD2, 0x0BD6, 0x0BDA, 0x0BDE, - 0x0BE2, 0x0BE6, 0x0BEA, 0x0BEE, 0x0BF2, 0x0BF6, 0x0BFA, 0x0BFE, - 0x0C02, 0x0C08, 0x0C0E, 0x0C14, 0x0C1A, 0x0C1E, 0x0C22, 0x0C26, - // Block 31 - 0x0C2A, 0x0C2E, 0x0C32, 0x0C36, 0x0C3A, 0x0C3E, 0x0C42, 0x0C46, - 0x0C4A, 0x0C4E, 0x0C52, 0x0C56, 0x0C5A, 0x0C5E, 0x0C62, 0x0C66, - 0x0C6A, 0x0C6E, 0x0C72, 0x0C76, 0x0C7A, 0x0C7E, 0x0C82, 0x0C86, - 0x0C8A, 0x0C8E, 0x0C92, 0x0B9A, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0C9B, 0x0C9F, 0x0CA3, 0x0CA7, 0x0CAB, 0x0CB1, 0x0CB7, 0x0CBD, - 0x0CC3, 0x0CC9, 0x0CCF, 0x0CD5, 0x0CDB, 0x0CE1, 0x0CE7, 0x0CED, - 0x0CF3, 0x0CF9, 0x0CFF, 0x0D05, 0x0D0B, 0x0D11, 0x0D17, 0x0D1D, - 0x0D23, 0x0D27, 0x0D2B, 0x0D2F, 0x0D33, 0x0D37, 0x0D3B, 0x0D41, - // Block 32 - 0x0D47, 0x0D4D, 0x0D53, 0x0D59, 0x0D5F, 0x0D65, 0x0D6B, 0x0D71, - 0x0D77, 0x0D7B, 0x0D7F, 0x0D83, 0x0D87, 0x0D8B, 0x0D8F, 0x0D93, - 0x0D97, 0x0D9D, 0x0DA3, 0x0DA9, 0x0DAF, 0x0DB5, 0x0DBB, 0x0DC1, - 0x0DC7, 0x0DCD, 0x0DD3, 0x0DD9, 0x0DDF, 0x0DE5, 0x0DEB, 0x0DF1, - 0x0DF7, 0x0DFD, 0x0E03, 0x0E09, 0x0E0F, 0x0E13, 0x0E17, 0x0E1B, - 0x0E1F, 0x0E25, 0x0E2B, 0x0E31, 0x0E37, 0x0E3D, 0x0E43, 0x0E49, - 0x0E4F, 0x0E55, 0x0E5B, 0x0E5F, 0x0E63, 0x0E67, 0x0E6B, 0x0E6F, - 0x0E73, 0x0E77, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 33 - 0x0E7B, 0x0E80, 0x0E85, 0x0E8C, 0x0E93, 0x0E9A, 0x0EA1, 0x0EA8, - 0x0EAF, 0x0EB4, 0x0EB9, 0x0EC0, 0x0EC7, 0x0ECE, 0x0ED5, 0x0EDC, - 0x0EE3, 0x0EE8, 0x0EED, 0x0EF4, 0x0EFB, 0x0F02, 0x0000, 0x0000, - 0x0F09, 0x0F0E, 0x0F13, 0x0F1A, 0x0F21, 0x0F28, 0x0000, 0x0000, - 0x0F2F, 0x0F34, 0x0F39, 0x0F40, 0x0F47, 0x0F4E, 0x0F55, 0x0F5C, - 0x0F63, 0x0F68, 0x0F6D, 0x0F74, 0x0F7B, 0x0F82, 0x0F89, 0x0F90, - 0x0F97, 0x0F9C, 0x0FA1, 0x0FA8, 0x0FAF, 0x0FB6, 0x0FBD, 0x0FC4, - 0x0FCB, 0x0FD0, 0x0FD5, 0x0FDC, 0x0FE3, 0x0FEA, 0x0FF1, 0x0FF8, - // Block 34 - 0x0FFF, 0x1004, 0x1009, 0x1010, 0x1017, 0x101E, 0x0000, 0x0000, - 0x1025, 0x102A, 0x102F, 0x1036, 0x103D, 0x1044, 0x0000, 0x0000, - 0x104B, 0x1050, 0x1055, 0x105C, 0x1063, 0x106A, 0x1071, 0x1078, - 0x0000, 0x107F, 0x0000, 0x1084, 0x0000, 0x108B, 0x0000, 0x1092, - 0x1099, 0x109E, 0x10A3, 0x10AA, 0x10B1, 0x10B8, 0x10BF, 0x10C6, - 0x10CD, 0x10D2, 0x10D7, 0x10DE, 0x10E5, 0x10EC, 0x10F3, 0x10FA, - 0x1101, 0x0525, 0x1106, 0x052A, 0x110B, 0x052F, 0x1110, 0x0534, - 0x1115, 0x054A, 0x111A, 0x054F, 0x111F, 0x0554, 0x0000, 0x0000, - // Block 35 - 0x1124, 0x112B, 0x1132, 0x113B, 0x1144, 0x114D, 0x1156, 0x115F, - 0x1168, 0x116F, 0x1176, 0x117F, 0x1188, 0x1191, 0x119A, 0x11A3, - 0x11AC, 0x11B3, 0x11BA, 0x11C3, 0x11CC, 0x11D5, 0x11DE, 0x11E7, - 0x11F0, 0x11F7, 0x11FE, 0x1207, 0x1210, 0x1219, 0x1222, 0x122B, - 0x1234, 0x123B, 0x1242, 0x124B, 0x1254, 0x125D, 0x1266, 0x126F, - 0x1278, 0x127F, 0x1286, 0x128F, 0x1298, 0x12A1, 0x12AA, 0x12B3, - 0x12BC, 0x12C1, 0x12C6, 0x12CD, 0x12D2, 0x0000, 0x12D9, 0x12DE, - 0x12E5, 0x12EA, 0x12EF, 0x04EE, 0x12F4, 0x12F9, 0x12FD, 0x12F9, - // Block 36 - 0x1300, 0x1309, 0x130F, 0x1316, 0x131B, 0x0000, 0x1322, 0x1327, - 0x132E, 0x04F6, 0x1333, 0x04FB, 0x1338, 0x1343, 0x134F, 0x135B, - 0x1361, 0x1366, 0x136B, 0x0514, 0x0000, 0x0000, 0x1372, 0x1377, - 0x137E, 0x1383, 0x1388, 0x0500, 0x0000, 0x1393, 0x139F, 0x13AB, - 0x13B1, 0x13B6, 0x13BB, 0x0539, 0x13C2, 0x13C7, 0x13CC, 0x13D1, - 0x13D8, 0x13DD, 0x13E2, 0x050A, 0x13E7, 0x13F1, 0x04E8, 0x13F7, - 0x0000, 0x0000, 0x13F9, 0x1400, 0x1405, 0x0000, 0x140C, 0x1411, - 0x1418, 0x0505, 0x141D, 0x050F, 0x1422, 0x0011, 0x142A, 0x0000, - // Block 37 - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x1436, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x143A, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x143E, 0x1440, 0x1443, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0000, 0x1447, 0x144E, 0x0000, 0x1458, 0x145F, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1469, 0x0000, 0x146C, 0x0000, - // Block 38 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1470, - 0x1473, 0x1476, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1479, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1486, 0x097C, 0x0000, 0x0000, 0x1488, 0x148A, 0x148C, 0x148E, - 0x1490, 0x1492, 0x1494, 0x1496, 0x149A, 0x149C, 0x149E, 0x14A0, - // Block 39 - 0x1486, 0x001C, 0x000D, 0x000F, 0x1488, 0x148A, 0x148C, 0x148E, - 0x1490, 0x1492, 0x1494, 0x1496, 0x149A, 0x149C, 0x149E, 0x0000, - 0x0007, 0x0941, 0x001E, 0x04C7, 0x0943, 0x0494, 0x094E, 0x04C5, - 0x0950, 0x14A0, 0x0960, 0x02C8, 0x0962, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x14A2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3A - 0x14A5, 0x14A9, 0x14AD, 0x14AF, 0x0000, 0x14B3, 0x14B7, 0x14BB, - 0x0000, 0x14BE, 0x094C, 0x0916, 0x0916, 0x0916, 0x0494, 0x14C2, - 0x0918, 0x0918, 0x091E, 0x04C5, 0x0000, 0x0922, 0x14C5, 0x0000, - 0x0000, 0x0929, 0x14C8, 0x092B, 0x092B, 0x092B, 0x0000, 0x0000, - 0x14CA, 0x14CD, 0x14D1, 0x0000, 0x14D4, 0x0000, 0x14D6, 0x0000, - 0x14D4, 0x0000, 0x091C, 0x0046, 0x090B, 0x14AD, 0x0000, 0x0941, - 0x090F, 0x14D9, 0x0000, 0x0920, 0x001E, 0x14DB, 0x14DE, 0x14E1, - 0x14E4, 0x097C, 0x0000, 0x14E7, 0x056F, 0x0973, 0x14EB, 0x14EE, - // Block 3B - 0x14F1, 0x0000, 0x0000, 0x0000, 0x0000, 0x090D, 0x093F, 0x0941, - 0x097C, 0x0499, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x14F5, 0x14FB, 0x1501, 0x1508, 0x150E, 0x1514, 0x151A, 0x1520, - 0x1526, 0x152C, 0x1532, 0x1538, 0x153E, 0x1544, 0x154A, 0x1550, - 0x0918, 0x1555, 0x1558, 0x155C, 0x155F, 0x1561, 0x1564, 0x1568, - 0x156D, 0x1570, 0x1572, 0x1575, 0x091E, 0x14AD, 0x090D, 0x0920, - 0x097C, 0x1579, 0x157C, 0x1580, 0x096D, 0x1583, 0x1586, 0x158A, - 0x158F, 0x04C7, 0x1592, 0x1595, 0x04C5, 0x0984, 0x093F, 0x0950, - // Block 3C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x1599, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x159F, 0x15A5, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x15AB, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x15B1, 0x15B7, 0x15BD, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3E - 0x0000, 0x0000, 0x0000, 0x0000, 0x15C3, 0x0000, 0x0000, 0x0000, - 0x0000, 0x15C9, 0x0000, 0x0000, 0x15CF, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x15D5, 0x0000, 0x15DB, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x15E1, 0x15E8, 0x0000, 0x15F2, - 0x15F9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3F - 0x0000, 0x1603, 0x0000, 0x0000, 0x1609, 0x0000, 0x0000, 0x160F, - 0x0000, 0x1615, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x161B, 0x0000, 0x161F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1625, 0x162B, 0x162F, - 0x1633, 0x1639, 0x0000, 0x0000, 0x163F, 0x1645, 0x0000, 0x0000, - 0x164B, 0x1651, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 40 - 0x1657, 0x165D, 0x0000, 0x0000, 0x1663, 0x1669, 0x0000, 0x0000, - 0x166F, 0x1675, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x167B, 0x1681, 0x1687, 0x168D, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 41 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1693, 0x1699, 0x169F, 0x16A5, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x16AB, 0x16B1, 0x16B7, 0x16BD, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 42 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x16C3, 0x16C7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 43 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x001C, 0x000D, 0x000F, 0x1488, 0x148A, 0x148C, 0x148E, 0x1490, - 0x1492, 0x16CB, 0x16CE, 0x16D1, 0x16D4, 0x16D7, 0x16DA, 0x16DD, - 0x16E0, 0x16E3, 0x16E6, 0x16E9, 0x16EC, 0x16F0, 0x16F4, 0x16F8, - 0x16FC, 0x1700, 0x1704, 0x1708, 0x170C, 0x1710, 0x1715, 0x171A, - // Block 44 - 0x171F, 0x1724, 0x1729, 0x172E, 0x1733, 0x1738, 0x173D, 0x1742, - 0x1747, 0x174A, 0x174D, 0x1750, 0x1753, 0x1756, 0x1759, 0x175C, - 0x175F, 0x1762, 0x1766, 0x176A, 0x176E, 0x1772, 0x1776, 0x177A, - 0x177E, 0x1782, 0x1786, 0x178A, 0x178E, 0x1792, 0x1796, 0x179A, - 0x179E, 0x17A2, 0x17A6, 0x17AA, 0x17AE, 0x17B2, 0x17B6, 0x17BA, - 0x17BE, 0x17C2, 0x17C6, 0x17CA, 0x17CE, 0x17D2, 0x17D6, 0x17DA, - 0x17DE, 0x17E2, 0x17E6, 0x17EA, 0x17EE, 0x17F2, 0x0906, 0x090B, - 0x14AD, 0x090D, 0x090F, 0x14D9, 0x0914, 0x0916, 0x0918, 0x091A, - // Block 45 - 0x091C, 0x091E, 0x0920, 0x0922, 0x0924, 0x0929, 0x14C8, 0x092B, - 0x17F6, 0x092D, 0x092F, 0x155F, 0x0931, 0x1570, 0x17F8, 0x14D4, - 0x0007, 0x093D, 0x0984, 0x093F, 0x0941, 0x098C, 0x094C, 0x0494, - 0x097C, 0x0499, 0x094E, 0x04C5, 0x0950, 0x14A0, 0x001E, 0x0960, - 0x17FA, 0x049B, 0x02C8, 0x0962, 0x0964, 0x096D, 0x04A6, 0x04C7, - 0x04A8, 0x09DF, 0x1486, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 46 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x17FC, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 47 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1809, 0x180D, 0x1810, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 48 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1814, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 49 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0499, 0x155F, 0x0000, 0x0000, - // Block 4A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x181A, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 4B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x181E, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 4C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x1822, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 4D - 0x1826, 0x182A, 0x182E, 0x1832, 0x1836, 0x183A, 0x183E, 0x1842, - 0x1846, 0x184A, 0x184E, 0x1852, 0x1856, 0x185A, 0x185E, 0x1862, - 0x1866, 0x186A, 0x186E, 0x1872, 0x1876, 0x187A, 0x187E, 0x1882, - 0x1886, 0x188A, 0x188E, 0x1892, 0x1896, 0x189A, 0x189E, 0x18A2, - 0x18A6, 0x18AA, 0x18AE, 0x18B2, 0x18B6, 0x18BA, 0x18BE, 0x18C2, - 0x18C6, 0x18CA, 0x18CE, 0x18D2, 0x18D6, 0x18DA, 0x18DE, 0x18E2, - 0x18E6, 0x18EA, 0x18EE, 0x18F2, 0x18F6, 0x18FA, 0x18FE, 0x1902, - 0x1906, 0x190A, 0x190E, 0x1912, 0x1916, 0x191A, 0x191E, 0x1922, - // Block 4E - 0x1926, 0x192A, 0x192E, 0x1932, 0x1936, 0x193A, 0x193E, 0x1942, - 0x1946, 0x194A, 0x194E, 0x1952, 0x1956, 0x195A, 0x195E, 0x1962, - 0x1966, 0x196A, 0x196E, 0x1972, 0x1976, 0x197A, 0x197E, 0x1982, - 0x1986, 0x198A, 0x198E, 0x1992, 0x1996, 0x199A, 0x199E, 0x19A2, - 0x19A6, 0x19AA, 0x19AE, 0x19B2, 0x19B6, 0x19BA, 0x19BE, 0x19C2, - 0x19C6, 0x19CA, 0x19CE, 0x19D2, 0x19D6, 0x19DA, 0x19DE, 0x19E2, - 0x19E6, 0x19EA, 0x19EE, 0x19F2, 0x19F6, 0x19FA, 0x19FE, 0x1A02, - 0x1A06, 0x1A0A, 0x1A0E, 0x1A12, 0x1A16, 0x1A1A, 0x1A1E, 0x1A22, - // Block 4F - 0x1A26, 0x1A2A, 0x1A2E, 0x1A32, 0x1A36, 0x1A3A, 0x1A3E, 0x1A42, - 0x1A46, 0x1A4A, 0x1A4E, 0x1A52, 0x1A56, 0x1A5A, 0x1A5E, 0x1A62, - 0x1A66, 0x1A6A, 0x1A6E, 0x1A72, 0x1A76, 0x1A7A, 0x1A7E, 0x1A82, - 0x1A86, 0x1A8A, 0x1A8E, 0x1A92, 0x1A96, 0x1A9A, 0x1A9E, 0x1AA2, - 0x1AA6, 0x1AAA, 0x1AAE, 0x1AB2, 0x1AB6, 0x1ABA, 0x1ABE, 0x1AC2, - 0x1AC6, 0x1ACA, 0x1ACE, 0x1AD2, 0x1AD6, 0x1ADA, 0x1ADE, 0x1AE2, - 0x1AE6, 0x1AEA, 0x1AEE, 0x1AF2, 0x1AF6, 0x1AFA, 0x1AFE, 0x1B02, - 0x1B06, 0x1B0A, 0x1B0E, 0x1B12, 0x1B16, 0x1B1A, 0x1B1E, 0x1B22, - // Block 50 - 0x1B26, 0x1B2A, 0x1B2E, 0x1B32, 0x1B36, 0x1B3A, 0x1B3E, 0x1B42, - 0x1B46, 0x1B4A, 0x1B4E, 0x1B52, 0x1B56, 0x1B5A, 0x1B5E, 0x1B62, - 0x1B66, 0x1B6A, 0x1B6E, 0x1B72, 0x1B76, 0x1B7A, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 51 - 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1B7E, 0x0000, - 0x1882, 0x1B82, 0x1B86, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 52 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1B8A, 0x0000, 0x1B91, 0x0000, - 0x1B98, 0x0000, 0x1B9F, 0x0000, 0x1BA6, 0x0000, 0x1BAD, 0x0000, - 0x1BB4, 0x0000, 0x1BBB, 0x0000, 0x1BC2, 0x0000, 0x1BC9, 0x0000, - 0x1BD0, 0x0000, 0x1BD7, 0x0000, 0x0000, 0x1BDE, 0x0000, 0x1BE5, - 0x0000, 0x1BEC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1BF3, 0x1BFA, 0x0000, 0x1C01, 0x1C08, 0x0000, 0x1C0F, 0x1C16, - 0x0000, 0x1C1D, 0x1C24, 0x0000, 0x1C2B, 0x1C32, 0x0000, 0x0000, - // Block 53 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1C39, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x1C40, 0x1C45, 0x0000, 0x1C4A, 0x1C51, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1C58, 0x0000, 0x1C5F, 0x0000, - 0x1C66, 0x0000, 0x1C6D, 0x0000, 0x1C74, 0x0000, 0x1C7B, 0x0000, - 0x1C82, 0x0000, 0x1C89, 0x0000, 0x1C90, 0x0000, 0x1C97, 0x0000, - // Block 54 - 0x1C9E, 0x0000, 0x1CA5, 0x0000, 0x0000, 0x1CAC, 0x0000, 0x1CB3, - 0x0000, 0x1CBA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1CC1, 0x1CC8, 0x0000, 0x1CCF, 0x1CD6, 0x0000, 0x1CDD, 0x1CE4, - 0x0000, 0x1CEB, 0x1CF2, 0x0000, 0x1CF9, 0x1D00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1D07, 0x0000, 0x0000, 0x1D0E, - 0x1D15, 0x1D1C, 0x1D23, 0x0000, 0x0000, 0x0000, 0x1D2A, 0x1D31, - // Block 55 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x1D38, 0x1D3C, 0x1D40, 0x1D44, 0x1D48, 0x1D4C, 0x1D50, - 0x1D54, 0x1D58, 0x1D5C, 0x1D60, 0x1D64, 0x1D68, 0x1D6C, 0x1D70, - // Block 56 - 0x1D74, 0x1D78, 0x1D7C, 0x1D80, 0x1D84, 0x1D88, 0x1D8C, 0x1D90, - 0x1D94, 0x1D98, 0x1D9C, 0x1DA0, 0x1DA4, 0x1DA8, 0x1DAC, 0x1DB0, - 0x1DB4, 0x1DB8, 0x1DBC, 0x1DC0, 0x1DC4, 0x1DC8, 0x1DCC, 0x1DD0, - 0x1DD4, 0x1DD8, 0x1DDC, 0x1DE0, 0x1DE4, 0x1DE8, 0x1DEC, 0x1DF0, - 0x1DF4, 0x1DF8, 0x1DFC, 0x1E00, 0x1E04, 0x1E08, 0x1E0C, 0x1E10, - 0x1E14, 0x1E18, 0x1E1C, 0x1E20, 0x1E24, 0x1E28, 0x1E2C, 0x1E30, - 0x1E34, 0x1E38, 0x1E3C, 0x1E40, 0x1E44, 0x1E48, 0x1E4C, 0x1E50, - 0x1E54, 0x1E58, 0x1E5C, 0x1E60, 0x1E64, 0x1E68, 0x1E6C, 0x1E70, - // Block 57 - 0x1E74, 0x1E78, 0x1E7C, 0x1E80, 0x1E84, 0x1E88, 0x1E8C, 0x1E90, - 0x1E94, 0x1E98, 0x1E9C, 0x1EA0, 0x1EA4, 0x1EA8, 0x1EAC, 0x0000, - 0x0000, 0x0000, 0x1826, 0x183E, 0x1EB0, 0x1EB4, 0x1EB8, 0x1EBC, - 0x1EC0, 0x1EC4, 0x1836, 0x1EC8, 0x1ECC, 0x1ED0, 0x1ED4, 0x1846, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 58 - 0x1ED8, 0x1EDE, 0x1EE4, 0x1EEA, 0x1EF0, 0x1EF6, 0x1EFC, 0x1F02, - 0x1F08, 0x1F0E, 0x1F14, 0x1F1A, 0x1F20, 0x1F26, 0x1F2C, 0x1F35, - 0x1F3E, 0x1F47, 0x1F50, 0x1F59, 0x1F62, 0x1F6B, 0x1F74, 0x1F7D, - 0x1F86, 0x1F8F, 0x1F98, 0x1FA1, 0x1FAA, 0x1FB3, 0x1FC5, 0x0000, - 0x1FD4, 0x1FDA, 0x1FE0, 0x1FE6, 0x1FEC, 0x1FF2, 0x1FF8, 0x1FFE, - 0x2004, 0x200A, 0x2010, 0x2016, 0x201C, 0x2022, 0x2028, 0x202E, - 0x2034, 0x203A, 0x2040, 0x2046, 0x204C, 0x2052, 0x2058, 0x205E, - 0x2064, 0x206A, 0x2070, 0x2076, 0x207C, 0x2082, 0x2088, 0x208E, - // Block 59 - 0x2094, 0x209A, 0x20A0, 0x20A6, 0x20AC, 0x20B0, 0x192E, 0x20B4, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x20B8, 0x20BC, 0x20BF, 0x20C2, 0x20C5, 0x20C8, 0x20CB, 0x20CE, - 0x20D1, 0x20D4, 0x20D7, 0x20DA, 0x20DD, 0x20E0, 0x20E3, 0x20E6, - 0x1D38, 0x1D44, 0x1D50, 0x1D58, 0x1D78, 0x1D7C, 0x1D88, 0x1D90, - 0x1D94, 0x1D9C, 0x1DA0, 0x1DA4, 0x1DA8, 0x1DAC, 0x20E9, 0x20F0, - 0x20F7, 0x20FE, 0x2105, 0x210C, 0x2113, 0x211A, 0x2121, 0x2128, - 0x212F, 0x2136, 0x213D, 0x2144, 0x214B, 0x215B, 0x2168, 0x0000, - // Block 5A - 0x1826, 0x183E, 0x1EB0, 0x1EB4, 0x216F, 0x2173, 0x2177, 0x1852, - 0x217B, 0x1882, 0x194A, 0x197A, 0x1976, 0x194E, 0x1ABE, 0x18A2, - 0x1942, 0x217F, 0x2183, 0x2187, 0x218B, 0x218F, 0x2193, 0x2197, - 0x219B, 0x219F, 0x21A3, 0x18BA, 0x21A7, 0x21AB, 0x21AF, 0x21B3, - 0x21B7, 0x21BB, 0x21BF, 0x21C3, 0x1EB8, 0x1EBC, 0x1EC0, 0x21C7, - 0x21CB, 0x21CF, 0x21D3, 0x21D7, 0x21DB, 0x21DF, 0x21E3, 0x21E7, - 0x21EB, 0x21EF, 0x21F2, 0x21F5, 0x21F8, 0x21FB, 0x21FE, 0x2201, - 0x2204, 0x2207, 0x220A, 0x220D, 0x2210, 0x2213, 0x2216, 0x2219, - // Block 5B - 0x221C, 0x2221, 0x2226, 0x222B, 0x2230, 0x2235, 0x223A, 0x223F, - 0x2244, 0x2249, 0x224F, 0x2255, 0x225B, 0x225E, 0x2262, 0x2265, - 0x2269, 0x226D, 0x2271, 0x2275, 0x2279, 0x227D, 0x2281, 0x2285, - 0x2289, 0x228D, 0x2291, 0x2295, 0x2299, 0x229D, 0x22A1, 0x22A5, - 0x22A9, 0x22AD, 0x22B1, 0x22B5, 0x22B9, 0x22BD, 0x22C1, 0x22C5, - 0x22C9, 0x22CD, 0x22D1, 0x22D5, 0x22D9, 0x22DD, 0x22E1, 0x22E5, - 0x22E9, 0x22ED, 0x22F1, 0x22F5, 0x22F9, 0x22FD, 0x2301, 0x2305, - 0x2309, 0x230D, 0x2311, 0x2315, 0x2319, 0x231D, 0x2321, 0x0000, - // Block 5C - 0x2325, 0x2335, 0x2342, 0x2352, 0x235C, 0x236C, 0x2376, 0x2380, - 0x2393, 0x23A0, 0x23AA, 0x23B4, 0x23BE, 0x23CB, 0x23D8, 0x23E5, - 0x23F2, 0x23FF, 0x240C, 0x2419, 0x242C, 0x2433, 0x2446, 0x2459, - 0x2469, 0x2476, 0x2489, 0x249C, 0x24A9, 0x24B3, 0x24BD, 0x24CA, - 0x24D7, 0x24E7, 0x24F7, 0x2501, 0x250B, 0x2518, 0x2522, 0x252C, - 0x2533, 0x253A, 0x2544, 0x254E, 0x2561, 0x256E, 0x257E, 0x2591, - 0x259E, 0x25A8, 0x25B2, 0x25C5, 0x25D2, 0x25E5, 0x25EF, 0x25FF, - 0x2609, 0x2616, 0x2620, 0x262D, 0x263D, 0x264A, 0x265A, 0x2667, - // Block 5D - 0x266E, 0x267E, 0x2688, 0x2692, 0x269F, 0x26A9, 0x26B3, 0x26BD, - 0x26CD, 0x26DA, 0x26E1, 0x26F4, 0x26FE, 0x270E, 0x271B, 0x2728, - 0x2732, 0x273C, 0x2749, 0x2750, 0x275D, 0x276D, 0x2774, 0x2787, - 0x2791, 0x2796, 0x279B, 0x27A0, 0x27A5, 0x27AA, 0x27AF, 0x27B4, - 0x27B9, 0x27BE, 0x27C3, 0x27C9, 0x27CF, 0x27D5, 0x27DB, 0x27E1, - 0x27E7, 0x27ED, 0x27F3, 0x27F9, 0x27FF, 0x2805, 0x280B, 0x2811, - 0x2817, 0x281D, 0x2821, 0x2824, 0x2827, 0x282B, 0x282E, 0x2831, - 0x2834, 0x2838, 0x283C, 0x283F, 0x2846, 0x284D, 0x2854, 0x285B, - // Block 5E - 0x2868, 0x286B, 0x286E, 0x2872, 0x2875, 0x2878, 0x287B, 0x287E, - 0x2881, 0x2885, 0x288A, 0x288D, 0x2890, 0x2894, 0x2898, 0x289B, - 0x289E, 0x28A1, 0x28A5, 0x28A9, 0x28AD, 0x28B1, 0x28B5, 0x28B8, - 0x28BB, 0x28BE, 0x28C1, 0x28C4, 0x28C8, 0x28CB, 0x28CE, 0x28D1, - 0x28D5, 0x28D9, 0x28DC, 0x28E0, 0x28E4, 0x28E8, 0x28EB, 0x28EF, - 0x28F5, 0x28FC, 0x28FF, 0x2903, 0x2907, 0x290B, 0x290F, 0x2917, - 0x2920, 0x2923, 0x2926, 0x292A, 0x292D, 0x2930, 0x2933, 0x2937, - 0x293A, 0x293D, 0x2940, 0x2943, 0x2946, 0x294A, 0x294D, 0x2950, - // Block 5F - 0x2953, 0x2957, 0x295B, 0x2960, 0x2963, 0x2966, 0x2969, 0x2970, - 0x2974, 0x2977, 0x297A, 0x297D, 0x2980, 0x2983, 0x2986, 0x2989, - 0x298C, 0x298F, 0x2992, 0x2996, 0x2999, 0x299C, 0x29A0, 0x29A4, - 0x29A7, 0x29AC, 0x29B0, 0x29B3, 0x29B6, 0x29B9, 0x29BC, 0x29C2, - 0x29C8, 0x29CD, 0x29D2, 0x29D7, 0x29DC, 0x29E1, 0x29E6, 0x29EB, - 0x29F0, 0x29F5, 0x29FB, 0x2A01, 0x2A07, 0x2A0D, 0x2A13, 0x2A19, - 0x2A1F, 0x2A25, 0x2A2B, 0x2A31, 0x2A37, 0x2A3D, 0x2A43, 0x2A49, - 0x2A4F, 0x2A55, 0x2A5B, 0x2A61, 0x2A67, 0x2A6D, 0x2A73, 0x2A79, - // Block 60 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x2A7D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 61 - 0x2A81, 0x2A85, 0x1A9E, 0x2A89, 0x2A8D, 0x2A91, 0x2A95, 0x1B76, - 0x1B76, 0x2A99, 0x1ABE, 0x2A9D, 0x2AA1, 0x2AA5, 0x2AA9, 0x2AAD, - 0x2AB1, 0x2AB5, 0x2AB9, 0x2ABD, 0x2AC1, 0x2AC5, 0x2AC9, 0x2ACD, - 0x2AD1, 0x2AD5, 0x2AD9, 0x2ADD, 0x2AE1, 0x2AE5, 0x2AE9, 0x2AED, - 0x2AF1, 0x2AF5, 0x2AF9, 0x2AFD, 0x2B01, 0x2B05, 0x2B09, 0x2B0D, - 0x2B11, 0x2B15, 0x2B19, 0x2B1D, 0x2B21, 0x2B25, 0x2B29, 0x2B2D, - 0x2B31, 0x2B35, 0x2B39, 0x2B3D, 0x1A16, 0x2B41, 0x2B45, 0x2B49, - 0x2B4D, 0x2B51, 0x2B55, 0x2B59, 0x2B5D, 0x2B61, 0x2B65, 0x2B69, - // Block 62 - 0x1B3A, 0x2B6D, 0x2B71, 0x2B75, 0x2B79, 0x2B7D, 0x2B81, 0x2B85, - 0x2B89, 0x2B8D, 0x2B91, 0x2B95, 0x2B99, 0x2B9D, 0x2BA1, 0x2BA5, - 0x2BA9, 0x2BAD, 0x2BB1, 0x2BB5, 0x2BB9, 0x2BBD, 0x2BC1, 0x2BC5, - 0x2BC9, 0x2BCD, 0x2BD1, 0x2BD5, 0x2AC1, 0x2BD9, 0x2BDD, 0x2BE1, - 0x2BE5, 0x2BE9, 0x2BED, 0x2BF1, 0x2BF5, 0x2BF9, 0x2BFD, 0x2C01, - 0x2C05, 0x2C09, 0x2C0D, 0x2C11, 0x2C15, 0x2C19, 0x2C1D, 0x2C21, - 0x2C25, 0x1AA6, 0x2C29, 0x2C2D, 0x2C31, 0x2C35, 0x2C39, 0x2C3D, - 0x2C41, 0x2C45, 0x2C49, 0x2C4D, 0x2C51, 0x2C55, 0x2C59, 0x2C5D, - // Block 63 - 0x2C61, 0x18BA, 0x2C65, 0x2C69, 0x2C6D, 0x2C71, 0x2C75, 0x2C79, - 0x2C7D, 0x2C81, 0x186E, 0x2C85, 0x2C89, 0x2C8D, 0x2C91, 0x2C95, - 0x2C99, 0x2C9D, 0x2CA1, 0x2CA5, 0x2CA9, 0x2CAD, 0x2CB1, 0x2CB5, - 0x2CB9, 0x2CBD, 0x2CC1, 0x2CC5, 0x2CC9, 0x2CCD, 0x2CD1, 0x2CD5, - 0x2CD9, 0x2C21, 0x2CDD, 0x2CE1, 0x2CE5, 0x2CE9, 0x2CED, 0x2CF1, - 0x2CF5, 0x2CF9, 0x2BE1, 0x2CFD, 0x2D01, 0x2D05, 0x2D09, 0x2D0D, - 0x2D11, 0x2D15, 0x2D19, 0x2D1D, 0x2D21, 0x2D25, 0x2D29, 0x2D2D, - 0x2D31, 0x2D35, 0x2D39, 0x2D3D, 0x2D41, 0x2D45, 0x2D49, 0x2AC1, - // Block 64 - 0x2D4D, 0x2D51, 0x2D55, 0x2D59, 0x1B72, 0x2D5D, 0x2D61, 0x2D65, - 0x2D69, 0x2D6D, 0x2D71, 0x2D75, 0x2D79, 0x2D7D, 0x2D81, 0x2D85, - 0x2D89, 0x2173, 0x2D8D, 0x2D91, 0x2D95, 0x2D99, 0x2D9D, 0x2DA1, - 0x2DA5, 0x2DA9, 0x2DAD, 0x2BE9, 0x2DB1, 0x2DB5, 0x2DB9, 0x2DBD, - 0x2DC1, 0x2DC5, 0x2DC9, 0x2DCD, 0x2DD1, 0x2DD5, 0x2DD9, 0x2DDD, - 0x2DE1, 0x1ABA, 0x2DE5, 0x2DE9, 0x2DED, 0x2DF1, 0x2DF5, 0x2DF9, - 0x2DFD, 0x2E01, 0x2E05, 0x2E09, 0x2E0D, 0x2E11, 0x2E15, 0x19F6, - 0x2E19, 0x2E1D, 0x2E21, 0x2E25, 0x2E29, 0x2E2D, 0x2E31, 0x2E35, - // Block 65 - 0x2E39, 0x2E3D, 0x2E41, 0x2E45, 0x2E49, 0x2E4D, 0x2E51, 0x2E55, - 0x1A62, 0x2E59, 0x1A6E, 0x2E5D, 0x2E61, 0x2E65, 0x0000, 0x0000, - 0x2E69, 0x0000, 0x2E6D, 0x0000, 0x0000, 0x2E71, 0x2E75, 0x2E79, - 0x2E7D, 0x2E81, 0x2E85, 0x2E89, 0x2E8D, 0x2E91, 0x1A12, 0x0000, - 0x2E95, 0x0000, 0x2E99, 0x0000, 0x0000, 0x2E9D, 0x2EA1, 0x0000, - 0x0000, 0x0000, 0x2EA5, 0x2EA9, 0x2EAD, 0x2EB1, 0x0000, 0x0000, - 0x2EB5, 0x2EB9, 0x2EBD, 0x2EC1, 0x2EC5, 0x2EC9, 0x2ECD, 0x2ED1, - 0x2ED5, 0x2ED9, 0x2EDD, 0x2EE1, 0x18D6, 0x2EE5, 0x2EE9, 0x2EED, - // Block 66 - 0x2EF1, 0x2EF5, 0x2EF9, 0x2EFD, 0x2F01, 0x2F05, 0x2F09, 0x2F0D, - 0x2F11, 0x2F15, 0x2F19, 0x2F1D, 0x2187, 0x2F21, 0x2F25, 0x2F29, - 0x2F2D, 0x2197, 0x2F31, 0x2F35, 0x2F39, 0x2F3D, 0x2F41, 0x2CB1, - 0x2F45, 0x2F49, 0x2F4D, 0x2F51, 0x2F55, 0x2F59, 0x2F59, 0x2F5D, - 0x2F61, 0x2F65, 0x2F69, 0x2F6D, 0x2F71, 0x2F75, 0x2F79, 0x2E9D, - 0x2F7D, 0x2F81, 0x2F85, 0x2F89, 0x2F8D, 0x2F92, 0x0000, 0x0000, - 0x2F96, 0x2F9A, 0x2F9E, 0x2FA2, 0x2FA6, 0x2FAA, 0x2FAE, 0x2FB2, - 0x2ECD, 0x2FB6, 0x2FBA, 0x2FBE, 0x2E69, 0x2FC2, 0x2FC6, 0x2FCA, - // Block 67 - 0x2FCE, 0x2FD2, 0x2FD6, 0x2FDA, 0x2FDE, 0x2FE2, 0x2FE6, 0x2FEA, - 0x2FEE, 0x2EED, 0x2FF2, 0x2EF1, 0x2FF6, 0x2FFA, 0x2FFE, 0x3002, - 0x3006, 0x2E6D, 0x2B15, 0x300A, 0x300E, 0x195A, 0x2C25, 0x2D71, - 0x3012, 0x3016, 0x2F0D, 0x301A, 0x2F11, 0x301E, 0x3022, 0x3026, - 0x2E75, 0x302A, 0x302E, 0x3032, 0x3036, 0x303A, 0x2E79, 0x303E, - 0x3042, 0x3046, 0x304A, 0x304E, 0x3052, 0x2F41, 0x3056, 0x305A, - 0x2CB1, 0x305E, 0x2F51, 0x3062, 0x3066, 0x306A, 0x306E, 0x3072, - 0x2F65, 0x3076, 0x2E99, 0x307A, 0x2F69, 0x2BD9, 0x307E, 0x2F6D, - // Block 68 - 0x3082, 0x2F75, 0x3086, 0x308A, 0x308E, 0x3092, 0x3096, 0x2F7D, - 0x2E8D, 0x309A, 0x2F81, 0x309E, 0x2F85, 0x30A2, 0x1B76, 0x30A6, - 0x30AB, 0x30B0, 0x30B5, 0x30B9, 0x30BD, 0x30C1, 0x30C6, 0x30CB, - 0x30D0, 0x30D4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 69 - 0x30D8, 0x30DB, 0x30DE, 0x30E1, 0x30E5, 0x30E9, 0x30E9, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x30EC, 0x30F1, 0x30F6, 0x30FB, 0x3100, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3105, 0x0000, 0x310A, - 0x310F, 0x14DB, 0x14E4, 0x3112, 0x3115, 0x3118, 0x311B, 0x311E, - 0x3121, 0x1494, 0x3124, 0x3129, 0x312E, 0x3135, 0x313C, 0x3141, - 0x3146, 0x314B, 0x3150, 0x3155, 0x315A, 0x315F, 0x3164, 0x0000, - 0x3169, 0x316E, 0x3173, 0x3178, 0x317D, 0x0000, 0x3182, 0x0000, - // Block 6A - 0x3187, 0x318C, 0x0000, 0x3191, 0x3196, 0x0000, 0x319B, 0x31A0, - 0x31A5, 0x31AA, 0x31AF, 0x31B4, 0x31B9, 0x31BE, 0x31C3, 0x31C8, - 0x31CD, 0x31CD, 0x31D0, 0x31D0, 0x31D0, 0x31D0, 0x31D3, 0x31D3, - 0x31D3, 0x31D3, 0x31D6, 0x31D6, 0x31D6, 0x31D6, 0x31D9, 0x31D9, - 0x31D9, 0x31D9, 0x31DC, 0x31DC, 0x31DC, 0x31DC, 0x31DF, 0x31DF, - 0x31DF, 0x31DF, 0x31E2, 0x31E2, 0x31E2, 0x31E2, 0x31E5, 0x31E5, - 0x31E5, 0x31E5, 0x31E8, 0x31E8, 0x31E8, 0x31E8, 0x31EB, 0x31EB, - 0x31EB, 0x31EB, 0x31EE, 0x31EE, 0x31EE, 0x31EE, 0x31F1, 0x31F1, - // Block 6B - 0x31F1, 0x31F1, 0x31F4, 0x31F4, 0x31F7, 0x31F7, 0x31FA, 0x31FA, - 0x31FD, 0x31FD, 0x3200, 0x3200, 0x3203, 0x3203, 0x3206, 0x3206, - 0x3206, 0x3206, 0x3209, 0x3209, 0x3209, 0x3209, 0x320C, 0x320C, - 0x320C, 0x320C, 0x320F, 0x320F, 0x320F, 0x320F, 0x3212, 0x3212, - 0x3215, 0x3215, 0x3215, 0x3215, 0x06BA, 0x06BA, 0x3218, 0x3218, - 0x3218, 0x3218, 0x321B, 0x321B, 0x321B, 0x321B, 0x321E, 0x321E, - 0x06C4, 0x06C4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 6C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3221, 0x3221, 0x3221, 0x3221, 0x3224, - 0x3224, 0x3227, 0x3227, 0x322A, 0x322A, 0x06B0, 0x322D, 0x322D, - 0x3230, 0x3230, 0x3233, 0x3233, 0x3236, 0x3236, 0x3236, 0x3236, - 0x3239, 0x3239, 0x323C, 0x323C, 0x3243, 0x3243, 0x324A, 0x324A, - 0x3251, 0x3251, 0x3258, 0x3258, 0x325F, 0x325F, 0x3266, 0x3266, - 0x3266, 0x326D, 0x326D, 0x326D, 0x3274, 0x3274, 0x3274, 0x3274, - // Block 6D - 0x3277, 0x327E, 0x3285, 0x326D, 0x328C, 0x3293, 0x3298, 0x329D, - 0x32A2, 0x32A7, 0x32AC, 0x32B1, 0x32B6, 0x32BB, 0x32C0, 0x32C5, - 0x32CA, 0x32CF, 0x32D4, 0x32D9, 0x32DE, 0x32E3, 0x32E8, 0x32ED, - 0x32F2, 0x32F7, 0x32FC, 0x3301, 0x3306, 0x330B, 0x3310, 0x3315, - 0x331A, 0x331F, 0x3324, 0x3329, 0x332E, 0x3333, 0x3338, 0x333D, - 0x3342, 0x3347, 0x334C, 0x3351, 0x3356, 0x335B, 0x3360, 0x3365, - 0x336A, 0x336F, 0x3374, 0x3379, 0x337E, 0x3383, 0x3388, 0x338D, - 0x3392, 0x3397, 0x339C, 0x33A1, 0x33A6, 0x33AB, 0x33B0, 0x33B5, - // Block 6E - 0x33BA, 0x33BF, 0x33C4, 0x33C9, 0x33CE, 0x33D3, 0x33D8, 0x33DD, - 0x33E2, 0x33E7, 0x33EC, 0x33F1, 0x33F6, 0x33FB, 0x3400, 0x3405, - 0x340A, 0x340F, 0x3414, 0x3419, 0x341E, 0x3423, 0x3428, 0x342D, - 0x3432, 0x3437, 0x343C, 0x3441, 0x3446, 0x344B, 0x3450, 0x3456, - 0x345C, 0x3462, 0x3468, 0x346E, 0x3474, 0x347B, 0x3285, 0x3482, - 0x326D, 0x328C, 0x3489, 0x348E, 0x32A2, 0x3493, 0x32A7, 0x32AC, - 0x3498, 0x349D, 0x32C0, 0x34A2, 0x32C5, 0x32CA, 0x34A7, 0x34AC, - 0x32D4, 0x34B1, 0x32D9, 0x32DE, 0x336F, 0x3374, 0x3383, 0x3388, - // Block 6F - 0x338D, 0x33A1, 0x33A6, 0x33AB, 0x33B0, 0x33C4, 0x33C9, 0x33CE, - 0x34B6, 0x33E2, 0x34BB, 0x34C0, 0x3400, 0x34C5, 0x3405, 0x340A, - 0x344B, 0x34CA, 0x34CF, 0x3432, 0x34D4, 0x3437, 0x343C, 0x3277, - 0x327E, 0x34D9, 0x3285, 0x34E0, 0x3293, 0x3298, 0x329D, 0x32A2, - 0x34E7, 0x32B1, 0x32B6, 0x32BB, 0x32C0, 0x34EC, 0x32D4, 0x32E3, - 0x32E8, 0x32ED, 0x32F2, 0x32F7, 0x3301, 0x3306, 0x330B, 0x3310, - 0x3315, 0x331A, 0x34F1, 0x331F, 0x3324, 0x3329, 0x332E, 0x3333, - 0x3338, 0x3342, 0x3347, 0x334C, 0x3351, 0x3356, 0x335B, 0x3360, - // Block 70 - 0x3365, 0x336A, 0x3379, 0x337E, 0x3392, 0x3397, 0x339C, 0x33A1, - 0x33A6, 0x33B5, 0x33BA, 0x33BF, 0x33C4, 0x34F6, 0x33D3, 0x33D8, - 0x33DD, 0x33E2, 0x33F1, 0x33F6, 0x33FB, 0x3400, 0x34FB, 0x340F, - 0x3414, 0x3500, 0x3423, 0x3428, 0x342D, 0x3432, 0x3505, 0x3285, - 0x34E0, 0x32A2, 0x34E7, 0x32C0, 0x34EC, 0x32D4, 0x350A, 0x3315, - 0x350F, 0x3514, 0x3519, 0x33A1, 0x33A6, 0x33C4, 0x3400, 0x34FB, - 0x3432, 0x3505, 0x351E, 0x3525, 0x352C, 0x3533, 0x3538, 0x353D, - 0x3542, 0x3547, 0x354C, 0x3551, 0x3556, 0x355B, 0x3560, 0x3565, - // Block 71 - 0x356A, 0x356F, 0x3574, 0x3579, 0x357E, 0x3583, 0x3588, 0x358D, - 0x3592, 0x3597, 0x359C, 0x35A1, 0x3514, 0x35A6, 0x35AB, 0x35B0, - 0x35B5, 0x3533, 0x3538, 0x353D, 0x3542, 0x3547, 0x354C, 0x3551, - 0x3556, 0x355B, 0x3560, 0x3565, 0x356A, 0x356F, 0x3574, 0x3579, - 0x357E, 0x3583, 0x3588, 0x358D, 0x3592, 0x3597, 0x359C, 0x35A1, - 0x3514, 0x35A6, 0x35AB, 0x35B0, 0x35B5, 0x3597, 0x359C, 0x35A1, - 0x3514, 0x350F, 0x3519, 0x333D, 0x3306, 0x330B, 0x3310, 0x3597, - 0x359C, 0x35A1, 0x333D, 0x3342, 0x35BA, 0x35BA, 0x0000, 0x0000, - // Block 72 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x35BF, 0x35C6, 0x35C6, 0x35CD, 0x35D4, 0x35DB, 0x35E2, 0x35E9, - 0x35F0, 0x35F0, 0x35F7, 0x35FE, 0x3605, 0x360C, 0x3613, 0x361A, - 0x361A, 0x3621, 0x3628, 0x3628, 0x362F, 0x362F, 0x3636, 0x363D, - 0x363D, 0x3644, 0x364B, 0x364B, 0x3652, 0x3652, 0x3659, 0x3660, - 0x3660, 0x3667, 0x3667, 0x366E, 0x3675, 0x367C, 0x3683, 0x3683, - 0x368A, 0x3691, 0x3698, 0x369F, 0x36A6, 0x36A6, 0x36AD, 0x36B4, - // Block 73 - 0x36BB, 0x36C2, 0x36C9, 0x36D0, 0x36D0, 0x36D7, 0x36D7, 0x36DE, - 0x36DE, 0x36E5, 0x36EC, 0x36F3, 0x36FA, 0x3701, 0x3708, 0x370F, - 0x0000, 0x0000, 0x3716, 0x371D, 0x3724, 0x372B, 0x3732, 0x3739, - 0x3739, 0x3740, 0x3747, 0x374E, 0x3755, 0x3755, 0x375C, 0x3763, - 0x376A, 0x3771, 0x3778, 0x377F, 0x3786, 0x378D, 0x3794, 0x379B, - 0x37A2, 0x37A9, 0x37B0, 0x37B7, 0x37BE, 0x37C5, 0x37CC, 0x37D3, - 0x37DA, 0x37E1, 0x37E8, 0x37EF, 0x36AD, 0x36BB, 0x37F6, 0x37FD, - 0x3804, 0x380B, 0x3812, 0x3819, 0x3812, 0x3804, 0x3820, 0x3827, - // Block 74 - 0x382E, 0x3835, 0x383C, 0x3819, 0x367C, 0x3636, 0x3843, 0x384A, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3851, 0x3858, 0x385F, 0x3868, 0x3871, 0x387A, 0x3883, 0x388C, - 0x3895, 0x389E, 0x38A5, 0x38C7, 0x38D7, 0x0000, 0x0000, 0x0000, - // Block 75 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x38E0, 0x38E2, 0x38E6, 0x38EA, 0x04E1, 0x38EC, 0x38EE, 0x38F0, - 0x38F4, 0x1443, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1440, 0x38F8, 0x38FC, 0x3900, 0x3900, 0x149C, 0x149E, 0x3902, - 0x3904, 0x3906, 0x390A, 0x390E, 0x3912, 0x3916, 0x391A, 0x16C3, - // Block 76 - 0x16C7, 0x391E, 0x3922, 0x3926, 0x392A, 0x0000, 0x0000, 0x392E, - 0x3930, 0x146C, 0x146C, 0x146C, 0x146C, 0x3900, 0x3900, 0x3900, - 0x38E0, 0x38E2, 0x143E, 0x0000, 0x04E1, 0x38EA, 0x38EE, 0x38EC, - 0x38F8, 0x149C, 0x149E, 0x3902, 0x3904, 0x3906, 0x390A, 0x3932, - 0x3934, 0x3936, 0x1494, 0x3938, 0x393A, 0x393C, 0x149A, 0x0000, - 0x393E, 0x3940, 0x3942, 0x3944, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3946, 0x394A, 0x394F, 0x0000, 0x3953, 0x0000, 0x3957, 0x395B, - 0x3960, 0x3964, 0x3969, 0x396D, 0x3972, 0x3976, 0x397B, 0x397F, - // Block 77 - 0x3984, 0x068D, 0x068D, 0x0692, 0x0692, 0x0697, 0x0697, 0x069C, - 0x069C, 0x06A1, 0x06A1, 0x06A1, 0x06A1, 0x3987, 0x3987, 0x398A, - 0x398A, 0x398A, 0x398A, 0x398D, 0x398D, 0x3990, 0x3990, 0x3990, - 0x3990, 0x3993, 0x3993, 0x3993, 0x3993, 0x3996, 0x3996, 0x3996, - 0x3996, 0x3999, 0x3999, 0x3999, 0x3999, 0x399C, 0x399C, 0x399C, - 0x399C, 0x399F, 0x399F, 0x39A2, 0x39A2, 0x39A5, 0x39A5, 0x39A8, - 0x39A8, 0x39AB, 0x39AB, 0x39AB, 0x39AB, 0x39AE, 0x39AE, 0x39AE, - 0x39AE, 0x39B1, 0x39B1, 0x39B1, 0x39B1, 0x39B4, 0x39B4, 0x39B4, - // Block 78 - 0x39B4, 0x39B7, 0x39B7, 0x39B7, 0x39B7, 0x39BA, 0x39BA, 0x39BA, - 0x39BA, 0x39BD, 0x39BD, 0x39BD, 0x39BD, 0x39C0, 0x39C0, 0x39C0, - 0x39C0, 0x39C3, 0x39C3, 0x39C3, 0x39C3, 0x39C6, 0x39C6, 0x39C6, - 0x39C6, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39CC, 0x39CC, 0x39CC, - 0x39CC, 0x39CF, 0x39CF, 0x39CF, 0x39CF, 0x39D2, 0x39D2, 0x39D2, - 0x39D2, 0x39D5, 0x39D5, 0x39D5, 0x39D5, 0x39D8, 0x39D8, 0x3239, - 0x3239, 0x39DB, 0x39DB, 0x39DB, 0x39DB, 0x39DE, 0x39DE, 0x39E5, - 0x39E5, 0x39EC, 0x39EC, 0x39F3, 0x39F3, 0x0000, 0x0000, 0x0000, - // Block 79 - 0x0000, 0x38EC, 0x39F8, 0x3932, 0x3940, 0x3942, 0x3934, 0x39FA, - 0x149C, 0x149E, 0x3936, 0x1494, 0x38E0, 0x3938, 0x143E, 0x39FC, - 0x1486, 0x001C, 0x000D, 0x000F, 0x1488, 0x148A, 0x148C, 0x148E, - 0x1490, 0x1492, 0x38EA, 0x04E1, 0x393A, 0x149A, 0x393C, 0x38EE, - 0x3944, 0x0906, 0x090B, 0x14AD, 0x090D, 0x090F, 0x14D9, 0x0914, - 0x0916, 0x0918, 0x091A, 0x091C, 0x091E, 0x0920, 0x0922, 0x0924, - 0x0929, 0x14C8, 0x092B, 0x17F6, 0x092D, 0x092F, 0x155F, 0x0931, - 0x1570, 0x17F8, 0x14D4, 0x392E, 0x393E, 0x3930, 0x39FE, 0x3900, - // Block 7A - 0x13F7, 0x0007, 0x093D, 0x0984, 0x093F, 0x0941, 0x098C, 0x094C, - 0x0494, 0x097C, 0x0499, 0x094E, 0x04C5, 0x0950, 0x14A0, 0x001E, - 0x0960, 0x17FA, 0x049B, 0x02C8, 0x0962, 0x0964, 0x096D, 0x04A6, - 0x04C7, 0x04A8, 0x09DF, 0x3902, 0x3A00, 0x3904, 0x3A02, 0x3A04, - 0x3A08, 0x38E6, 0x391E, 0x3922, 0x38E2, 0x3A0C, 0x2321, 0x3A10, - 0x3A14, 0x3A18, 0x3A1C, 0x3A20, 0x3A24, 0x3A28, 0x3A2C, 0x3A30, - 0x3A34, 0x2269, 0x226D, 0x2271, 0x2275, 0x2279, 0x227D, 0x2281, - 0x2285, 0x2289, 0x228D, 0x2291, 0x2295, 0x2299, 0x229D, 0x22A1, - // Block 7B - 0x22A5, 0x22A9, 0x22AD, 0x22B1, 0x22B5, 0x22B9, 0x22BD, 0x22C1, - 0x22C5, 0x22C9, 0x22CD, 0x22D1, 0x22D5, 0x22D9, 0x22DD, 0x22E1, - 0x22E5, 0x22E9, 0x22ED, 0x22F1, 0x22F5, 0x22F9, 0x22FD, 0x2301, - 0x2305, 0x2309, 0x230D, 0x2311, 0x2315, 0x3A38, 0x3A3C, 0x3A40, - 0x1E04, 0x1D38, 0x1D3C, 0x1D40, 0x1D44, 0x1D48, 0x1D4C, 0x1D50, - 0x1D54, 0x1D58, 0x1D5C, 0x1D60, 0x1D64, 0x1D68, 0x1D6C, 0x1D70, - 0x1D74, 0x1D78, 0x1D7C, 0x1D80, 0x1D84, 0x1D88, 0x1D8C, 0x1D90, - 0x1D94, 0x1D98, 0x1D9C, 0x1DA0, 0x1DA4, 0x1DA8, 0x1DAC, 0x0000, - // Block 7C - 0x0000, 0x0000, 0x1DB0, 0x1DB4, 0x1DB8, 0x1DBC, 0x1DC0, 0x1DC4, - 0x0000, 0x0000, 0x1DC8, 0x1DCC, 0x1DD0, 0x1DD4, 0x1DD8, 0x1DDC, - 0x0000, 0x0000, 0x1DE0, 0x1DE4, 0x1DE8, 0x1DEC, 0x1DF0, 0x1DF4, - 0x0000, 0x0000, 0x1DF8, 0x1DFC, 0x1E00, 0x0000, 0x0000, 0x0000, - 0x3A44, 0x3A47, 0x3A4A, 0x0009, 0x3A4D, 0x3A50, 0x3A53, 0x0000, - 0x3A57, 0x3A5B, 0x3A5F, 0x3A63, 0x3A67, 0x3A6B, 0x3A6F, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 7D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x3A73, 0x0000, 0x3A7C, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3A85, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 7E - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3A8E, 0x3A97, - 0x3AA0, 0x3AAD, 0x3ABA, 0x3AC7, 0x3AD4, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 7F - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3AE1, 0x3AEA, 0x3AF3, 0x3B00, 0x3B0D, - // Block 80 - 0x3B1A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 81 - 0x0906, 0x090B, 0x14AD, 0x090D, 0x090F, 0x14D9, 0x0914, 0x0916, - 0x0918, 0x091A, 0x091C, 0x091E, 0x0920, 0x0922, 0x0924, 0x0929, - 0x14C8, 0x092B, 0x17F6, 0x092D, 0x092F, 0x155F, 0x0931, 0x1570, - 0x17F8, 0x14D4, 0x0007, 0x093D, 0x0984, 0x093F, 0x0941, 0x098C, - 0x094C, 0x0494, 0x097C, 0x0499, 0x094E, 0x04C5, 0x0950, 0x14A0, - 0x001E, 0x0960, 0x17FA, 0x049B, 0x02C8, 0x0962, 0x0964, 0x096D, - 0x04A6, 0x04C7, 0x04A8, 0x09DF, 0x0906, 0x090B, 0x14AD, 0x090D, - 0x090F, 0x14D9, 0x0914, 0x0916, 0x0918, 0x091A, 0x091C, 0x091E, - // Block 82 - 0x0920, 0x0922, 0x0924, 0x0929, 0x14C8, 0x092B, 0x17F6, 0x092D, - 0x092F, 0x155F, 0x0931, 0x1570, 0x17F8, 0x14D4, 0x0007, 0x093D, - 0x0984, 0x093F, 0x0941, 0x098C, 0x094C, 0x0000, 0x097C, 0x0499, - 0x094E, 0x04C5, 0x0950, 0x14A0, 0x001E, 0x0960, 0x17FA, 0x049B, - 0x02C8, 0x0962, 0x0964, 0x096D, 0x04A6, 0x04C7, 0x04A8, 0x09DF, - 0x0906, 0x090B, 0x14AD, 0x090D, 0x090F, 0x14D9, 0x0914, 0x0916, - 0x0918, 0x091A, 0x091C, 0x091E, 0x0920, 0x0922, 0x0924, 0x0929, - 0x14C8, 0x092B, 0x17F6, 0x092D, 0x092F, 0x155F, 0x0931, 0x1570, - // Block 83 - 0x17F8, 0x14D4, 0x0007, 0x093D, 0x0984, 0x093F, 0x0941, 0x098C, - 0x094C, 0x0494, 0x097C, 0x0499, 0x094E, 0x04C5, 0x0950, 0x14A0, - 0x001E, 0x0960, 0x17FA, 0x049B, 0x02C8, 0x0962, 0x0964, 0x096D, - 0x04A6, 0x04C7, 0x04A8, 0x09DF, 0x0906, 0x0000, 0x14AD, 0x090D, - 0x0000, 0x0000, 0x0914, 0x0000, 0x0000, 0x091A, 0x091C, 0x0000, - 0x0000, 0x0922, 0x0924, 0x0929, 0x14C8, 0x0000, 0x17F6, 0x092D, - 0x092F, 0x155F, 0x0931, 0x1570, 0x17F8, 0x14D4, 0x0007, 0x093D, - 0x0984, 0x093F, 0x0000, 0x098C, 0x0000, 0x0494, 0x097C, 0x0499, - // Block 84 - 0x094E, 0x04C5, 0x0950, 0x14A0, 0x0000, 0x0960, 0x17FA, 0x049B, - 0x02C8, 0x0962, 0x0964, 0x096D, 0x04A6, 0x04C7, 0x04A8, 0x09DF, - 0x0906, 0x090B, 0x14AD, 0x090D, 0x090F, 0x14D9, 0x0914, 0x0916, - 0x0918, 0x091A, 0x091C, 0x091E, 0x0920, 0x0922, 0x0924, 0x0929, - 0x14C8, 0x092B, 0x17F6, 0x092D, 0x092F, 0x155F, 0x0931, 0x1570, - 0x17F8, 0x14D4, 0x0007, 0x093D, 0x0984, 0x093F, 0x0941, 0x098C, - 0x094C, 0x0494, 0x097C, 0x0499, 0x094E, 0x04C5, 0x0950, 0x14A0, - 0x001E, 0x0960, 0x17FA, 0x049B, 0x02C8, 0x0962, 0x0964, 0x096D, - // Block 85 - 0x04A6, 0x04C7, 0x04A8, 0x09DF, 0x0906, 0x090B, 0x0000, 0x090D, - 0x090F, 0x14D9, 0x0914, 0x0000, 0x0000, 0x091A, 0x091C, 0x091E, - 0x0920, 0x0922, 0x0924, 0x0929, 0x14C8, 0x0000, 0x17F6, 0x092D, - 0x092F, 0x155F, 0x0931, 0x1570, 0x17F8, 0x0000, 0x0007, 0x093D, - 0x0984, 0x093F, 0x0941, 0x098C, 0x094C, 0x0494, 0x097C, 0x0499, - 0x094E, 0x04C5, 0x0950, 0x14A0, 0x001E, 0x0960, 0x17FA, 0x049B, - 0x02C8, 0x0962, 0x0964, 0x096D, 0x04A6, 0x04C7, 0x04A8, 0x09DF, - 0x0906, 0x090B, 0x0000, 0x090D, 0x090F, 0x14D9, 0x0914, 0x0000, - // Block 86 - 0x0918, 0x091A, 0x091C, 0x091E, 0x0920, 0x0000, 0x0924, 0x0000, - 0x0000, 0x0000, 0x17F6, 0x092D, 0x092F, 0x155F, 0x0931, 0x1570, - 0x17F8, 0x0000, 0x0007, 0x093D, 0x0984, 0x093F, 0x0941, 0x098C, - 0x094C, 0x0494, 0x097C, 0x0499, 0x094E, 0x04C5, 0x0950, 0x14A0, - 0x001E, 0x0960, 0x17FA, 0x049B, 0x02C8, 0x0962, 0x0964, 0x096D, - 0x04A6, 0x04C7, 0x04A8, 0x09DF, 0x0906, 0x090B, 0x14AD, 0x090D, - 0x090F, 0x14D9, 0x0914, 0x0916, 0x0918, 0x091A, 0x091C, 0x091E, - 0x0920, 0x0922, 0x0924, 0x0929, 0x14C8, 0x092B, 0x17F6, 0x092D, - // Block 87 - 0x092F, 0x155F, 0x0931, 0x1570, 0x17F8, 0x14D4, 0x0007, 0x093D, - 0x0984, 0x093F, 0x0941, 0x098C, 0x094C, 0x0494, 0x097C, 0x0499, - 0x094E, 0x04C5, 0x0950, 0x14A0, 0x001E, 0x0960, 0x17FA, 0x049B, - 0x02C8, 0x0962, 0x0964, 0x096D, 0x04A6, 0x04C7, 0x04A8, 0x09DF, - 0x0906, 0x090B, 0x14AD, 0x090D, 0x090F, 0x14D9, 0x0914, 0x0916, - 0x0918, 0x091A, 0x091C, 0x091E, 0x0920, 0x0922, 0x0924, 0x0929, - 0x14C8, 0x092B, 0x17F6, 0x092D, 0x092F, 0x155F, 0x0931, 0x1570, - 0x17F8, 0x14D4, 0x0007, 0x093D, 0x0984, 0x093F, 0x0941, 0x098C, - // Block 88 - 0x094C, 0x0494, 0x097C, 0x0499, 0x094E, 0x04C5, 0x0950, 0x14A0, - 0x001E, 0x0960, 0x17FA, 0x049B, 0x02C8, 0x0962, 0x0964, 0x096D, - 0x04A6, 0x04C7, 0x04A8, 0x09DF, 0x0906, 0x090B, 0x14AD, 0x090D, - 0x090F, 0x14D9, 0x0914, 0x0916, 0x0918, 0x091A, 0x091C, 0x091E, - 0x0920, 0x0922, 0x0924, 0x0929, 0x14C8, 0x092B, 0x17F6, 0x092D, - 0x092F, 0x155F, 0x0931, 0x1570, 0x17F8, 0x14D4, 0x0007, 0x093D, - 0x0984, 0x093F, 0x0941, 0x098C, 0x094C, 0x0494, 0x097C, 0x0499, - 0x094E, 0x04C5, 0x0950, 0x14A0, 0x001E, 0x0960, 0x17FA, 0x049B, - // Block 89 - 0x02C8, 0x0962, 0x0964, 0x096D, 0x04A6, 0x04C7, 0x04A8, 0x09DF, - 0x0906, 0x090B, 0x14AD, 0x090D, 0x090F, 0x14D9, 0x0914, 0x0916, - 0x0918, 0x091A, 0x091C, 0x091E, 0x0920, 0x0922, 0x0924, 0x0929, - 0x14C8, 0x092B, 0x17F6, 0x092D, 0x092F, 0x155F, 0x0931, 0x1570, - 0x17F8, 0x14D4, 0x0007, 0x093D, 0x0984, 0x093F, 0x0941, 0x098C, - 0x094C, 0x0494, 0x097C, 0x0499, 0x094E, 0x04C5, 0x0950, 0x14A0, - 0x001E, 0x0960, 0x17FA, 0x049B, 0x02C8, 0x0962, 0x0964, 0x096D, - 0x04A6, 0x04C7, 0x04A8, 0x09DF, 0x0906, 0x090B, 0x14AD, 0x090D, - // Block 8A - 0x090F, 0x14D9, 0x0914, 0x0916, 0x0918, 0x091A, 0x091C, 0x091E, - 0x0920, 0x0922, 0x0924, 0x0929, 0x14C8, 0x092B, 0x17F6, 0x092D, - 0x092F, 0x155F, 0x0931, 0x1570, 0x17F8, 0x14D4, 0x0007, 0x093D, - 0x0984, 0x093F, 0x0941, 0x098C, 0x094C, 0x0494, 0x097C, 0x0499, - 0x094E, 0x04C5, 0x0950, 0x14A0, 0x001E, 0x0960, 0x17FA, 0x049B, - 0x02C8, 0x0962, 0x0964, 0x096D, 0x04A6, 0x04C7, 0x04A8, 0x09DF, - 0x0906, 0x090B, 0x14AD, 0x090D, 0x090F, 0x14D9, 0x0914, 0x0916, - 0x0918, 0x091A, 0x091C, 0x091E, 0x0920, 0x0922, 0x0924, 0x0929, - // Block 8B - 0x14C8, 0x092B, 0x17F6, 0x092D, 0x092F, 0x155F, 0x0931, 0x1570, - 0x17F8, 0x14D4, 0x0007, 0x093D, 0x0984, 0x093F, 0x0941, 0x098C, - 0x094C, 0x0494, 0x097C, 0x0499, 0x094E, 0x04C5, 0x0950, 0x14A0, - 0x001E, 0x0960, 0x17FA, 0x049B, 0x02C8, 0x0962, 0x0964, 0x096D, - 0x04A6, 0x04C7, 0x04A8, 0x09DF, 0x3B27, 0x3B2A, 0x0000, 0x0000, - 0x3B2D, 0x3B30, 0x14EB, 0x3B33, 0x3B36, 0x3B39, 0x3B3C, 0x057B, - 0x3B3F, 0x3B42, 0x3B45, 0x3B48, 0x3B4B, 0x3B4E, 0x3B51, 0x14EE, - 0x3B54, 0x057B, 0x0581, 0x3B57, 0x055F, 0x3B5A, 0x3B5D, 0x3B60, - // Block 8C - 0x14D6, 0x3B63, 0x3B67, 0x0559, 0x0973, 0x0976, 0x057E, 0x3B6A, - 0x3B6D, 0x055C, 0x12FD, 0x0572, 0x3B70, 0x0015, 0x3B73, 0x3B76, - 0x3B79, 0x056F, 0x0575, 0x0578, 0x3B7C, 0x3B7F, 0x3B82, 0x056C, - 0x0979, 0x3B85, 0x3B88, 0x3B8B, 0x057E, 0x055C, 0x0572, 0x056C, - 0x0575, 0x056F, 0x3B2D, 0x3B30, 0x14EB, 0x3B33, 0x3B36, 0x3B39, - 0x3B3C, 0x057B, 0x3B3F, 0x3B42, 0x3B45, 0x3B48, 0x3B4B, 0x3B4E, - 0x3B51, 0x14EE, 0x3B54, 0x057B, 0x0581, 0x3B57, 0x055F, 0x3B5A, - 0x3B5D, 0x3B60, 0x14D6, 0x3B63, 0x3B67, 0x0559, 0x0973, 0x0976, - // Block 8D - 0x057E, 0x3B6A, 0x3B6D, 0x055C, 0x12FD, 0x0572, 0x3B70, 0x0015, - 0x3B73, 0x3B76, 0x3B79, 0x056F, 0x0575, 0x0578, 0x3B7C, 0x3B7F, - 0x3B82, 0x056C, 0x0979, 0x3B85, 0x3B88, 0x3B8B, 0x057E, 0x055C, - 0x0572, 0x056C, 0x0575, 0x056F, 0x3B2D, 0x3B30, 0x14EB, 0x3B33, - 0x3B36, 0x3B39, 0x3B3C, 0x057B, 0x3B3F, 0x3B42, 0x3B45, 0x3B48, - 0x3B4B, 0x3B4E, 0x3B51, 0x14EE, 0x3B54, 0x057B, 0x0581, 0x3B57, - 0x055F, 0x3B5A, 0x3B5D, 0x3B60, 0x14D6, 0x3B63, 0x3B67, 0x0559, - 0x0973, 0x0976, 0x057E, 0x3B6A, 0x3B6D, 0x055C, 0x12FD, 0x0572, - // Block 8E - 0x3B70, 0x0015, 0x3B73, 0x3B76, 0x3B79, 0x056F, 0x0575, 0x0578, - 0x3B7C, 0x3B7F, 0x3B82, 0x056C, 0x0979, 0x3B85, 0x3B88, 0x3B8B, - 0x057E, 0x055C, 0x0572, 0x056C, 0x0575, 0x056F, 0x3B2D, 0x3B30, - 0x14EB, 0x3B33, 0x3B36, 0x3B39, 0x3B3C, 0x057B, 0x3B3F, 0x3B42, - 0x3B45, 0x3B48, 0x3B4B, 0x3B4E, 0x3B51, 0x14EE, 0x3B54, 0x057B, - 0x0581, 0x3B57, 0x055F, 0x3B5A, 0x3B5D, 0x3B60, 0x14D6, 0x3B63, - 0x3B67, 0x0559, 0x0973, 0x0976, 0x057E, 0x3B6A, 0x3B6D, 0x055C, - 0x12FD, 0x0572, 0x3B70, 0x0015, 0x3B73, 0x3B76, 0x3B79, 0x056F, - // Block 8F - 0x0575, 0x0578, 0x3B7C, 0x3B7F, 0x3B82, 0x056C, 0x0979, 0x3B85, - 0x3B88, 0x3B8B, 0x057E, 0x055C, 0x0572, 0x056C, 0x0575, 0x056F, - 0x3B2D, 0x3B30, 0x14EB, 0x3B33, 0x3B36, 0x3B39, 0x3B3C, 0x057B, - 0x3B3F, 0x3B42, 0x3B45, 0x3B48, 0x3B4B, 0x3B4E, 0x3B51, 0x14EE, - 0x3B54, 0x057B, 0x0581, 0x3B57, 0x055F, 0x3B5A, 0x3B5D, 0x3B60, - 0x14D6, 0x3B63, 0x3B67, 0x0559, 0x0973, 0x0976, 0x057E, 0x3B6A, - 0x3B6D, 0x055C, 0x12FD, 0x0572, 0x3B70, 0x0015, 0x3B73, 0x3B76, - 0x3B79, 0x056F, 0x0575, 0x0578, 0x3B7C, 0x3B7F, 0x3B82, 0x056C, - // Block 90 - 0x0979, 0x3B85, 0x3B88, 0x3B8B, 0x057E, 0x055C, 0x0572, 0x056C, - 0x0575, 0x056F, 0x3B8F, 0x3B92, 0x0000, 0x0000, 0x1486, 0x001C, - 0x000D, 0x000F, 0x1488, 0x148A, 0x148C, 0x148E, 0x1490, 0x1492, - 0x1486, 0x001C, 0x000D, 0x000F, 0x1488, 0x148A, 0x148C, 0x148E, - 0x1490, 0x1492, 0x1486, 0x001C, 0x000D, 0x000F, 0x1488, 0x148A, - 0x148C, 0x148E, 0x1490, 0x1492, 0x1486, 0x001C, 0x000D, 0x000F, - 0x1488, 0x148A, 0x148C, 0x148E, 0x1490, 0x1492, 0x1486, 0x001C, - 0x000D, 0x000F, 0x1488, 0x148A, 0x148C, 0x148E, 0x1490, 0x1492, - // Block 91 - 0x3B95, 0x3B98, 0x3B9B, 0x3B9E, 0x3BA1, 0x3BA4, 0x3BA7, 0x3BAA, - 0x3BAD, 0x3BB0, 0x3BB3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3BB6, 0x3BBA, 0x3BBE, 0x3BC2, 0x3BC6, 0x3BCA, 0x3BCE, 0x3BD2, - 0x3BD6, 0x3BDA, 0x3BDE, 0x3BE2, 0x3BE6, 0x3BEA, 0x3BEE, 0x3BF2, - 0x3BF6, 0x3BFA, 0x3BFE, 0x3C02, 0x3C06, 0x3C0A, 0x3C0E, 0x3C12, - 0x3C16, 0x3C1A, 0x3C1E, 0x14AD, 0x092B, 0x3C26, 0x3C29, 0x0000, - 0x0906, 0x090B, 0x14AD, 0x090D, 0x090F, 0x14D9, 0x0914, 0x0916, - 0x0918, 0x091A, 0x091C, 0x091E, 0x0920, 0x0922, 0x0924, 0x0929, - // Block 92 - 0x14C8, 0x092B, 0x17F6, 0x092D, 0x092F, 0x155F, 0x0931, 0x1570, - 0x17F8, 0x14D4, 0x3C2C, 0x293D, 0x3C2F, 0x3C32, 0x3C35, 0x3C39, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 93 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3C3C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 94 - 0x3C3F, 0x3C46, 0x2291, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1922, 0x3C4D, 0x3C51, 0x1CB3, 0x183E, 0x3C55, 0x3C59, 0x1ED0, - 0x3C5D, 0x3C61, 0x3C65, 0x2D49, 0x3C69, 0x3C6D, 0x3C71, 0x3C75, - 0x3C79, 0x3C7D, 0x19B2, 0x3C81, 0x3C85, 0x3C89, 0x3C8D, 0x3C91, - 0x3C95, 0x1826, 0x1EB0, 0x3C99, 0x21C7, 0x1EBC, 0x21CB, 0x3C9D, - 0x1A92, 0x3CA1, 0x3CA5, 0x3CA9, 0x3CAD, 0x3CB1, 0x2183, 0x194A, - 0x3CB5, 0x3CB9, 0x3CBD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 95 - 0x3CC1, 0x3CCB, 0x3CD5, 0x3CDF, 0x3CE9, 0x3CF3, 0x3CFD, 0x3D07, - 0x3D11, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3D1B, 0x3D1F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 96 - 0x3D23, 0x3D27, 0x3D2B, 0x3D2F, 0x3D34, 0x2EB5, 0x3D38, 0x3D3C, - 0x3D40, 0x3D44, 0x2EB9, 0x3D48, 0x3D4C, 0x3D50, 0x2EBD, 0x3D55, - 0x3D59, 0x3D5D, 0x3D61, 0x3D66, 0x3D6A, 0x3C71, 0x3D6E, 0x3D73, - 0x3D77, 0x3D7B, 0x3D7F, 0x2F9A, 0x3D83, 0x1866, 0x3D88, 0x3D8C, - 0x3D90, 0x3D94, 0x3CB9, 0x3D98, 0x3D9C, 0x2FAE, 0x2EC1, 0x2EC5, - 0x2FB2, 0x3DA0, 0x3DA4, 0x2BF1, 0x3DA8, 0x2EC9, 0x3DAC, 0x3DB0, - 0x3DB4, 0x3DB8, 0x3DB8, 0x3DB8, 0x3DBC, 0x3DC1, 0x3DC5, 0x3DC9, - 0x3DCD, 0x3DD2, 0x3DD6, 0x3DDA, 0x3DDE, 0x3DE2, 0x3DE6, 0x3DEA, - // Block 97 - 0x3DEE, 0x3DF2, 0x3DF6, 0x3DFA, 0x3DFE, 0x3E02, 0x3E02, 0x2FBA, - 0x3E06, 0x3E0A, 0x3E0E, 0x3E12, 0x2ED1, 0x3E16, 0x3E1A, 0x3E1E, - 0x2E39, 0x3E22, 0x3E26, 0x3E2A, 0x3E2E, 0x3E32, 0x3E36, 0x3E3A, - 0x3E3E, 0x3E42, 0x3E47, 0x3E4B, 0x3E4F, 0x3C55, 0x3E53, 0x3E57, - 0x3E5B, 0x3E60, 0x3E65, 0x3E69, 0x3E6D, 0x3E71, 0x3E75, 0x3E79, - 0x3E7D, 0x3E81, 0x3E85, 0x3E85, 0x3E89, 0x3E8E, 0x3E92, 0x2BE1, - 0x3E96, 0x3E9A, 0x3E9F, 0x3EA3, 0x3EA7, 0x18CE, 0x3EAB, 0x3EAF, - 0x18D6, 0x3EB3, 0x3EB7, 0x3EBB, 0x3EC0, 0x3EC4, 0x3EC9, 0x3ECD, - // Block 98 - 0x3ED1, 0x3ED5, 0x3ED9, 0x3EDD, 0x3EE1, 0x3EE5, 0x3EE9, 0x3EED, - 0x3EF1, 0x3EF5, 0x3EFA, 0x3EFE, 0x3F02, 0x3F06, 0x2B11, 0x3F0A, - 0x18FE, 0x3F0F, 0x3F0F, 0x3F14, 0x3F18, 0x3F18, 0x3F1C, 0x3F20, - 0x3F25, 0x3F2A, 0x3F2E, 0x3F32, 0x3F36, 0x3F3A, 0x3F3E, 0x3F42, - 0x3F46, 0x3F4A, 0x3F4E, 0x2EE5, 0x3F52, 0x3F57, 0x3F5B, 0x3F5F, - 0x2FEA, 0x3F5F, 0x3F63, 0x2EED, 0x3F67, 0x3F6B, 0x3F6F, 0x3F73, - 0x2EF1, 0x2AA5, 0x3F77, 0x3F7B, 0x3F7F, 0x3F83, 0x3F87, 0x3F8B, - 0x3F8F, 0x3F94, 0x3F98, 0x3F9C, 0x3FA0, 0x3FA4, 0x3FA8, 0x3FAD, - // Block 99 - 0x3FB1, 0x3FB5, 0x3FB9, 0x3FBD, 0x3FC1, 0x3FC5, 0x3FC9, 0x3FCD, - 0x2EF5, 0x3FD1, 0x3FD5, 0x3FDA, 0x3FDE, 0x3FE2, 0x3FE6, 0x2EFD, - 0x3FEA, 0x3FEE, 0x3FF2, 0x3FF6, 0x3FFA, 0x3FFE, 0x4002, 0x4006, - 0x2B15, 0x300A, 0x400A, 0x400E, 0x4012, 0x4016, 0x401B, 0x401F, - 0x4023, 0x4027, 0x2F01, 0x402B, 0x4030, 0x4034, 0x4038, 0x30B5, - 0x403C, 0x4040, 0x4044, 0x4048, 0x404C, 0x4051, 0x4055, 0x4059, - 0x405D, 0x4062, 0x4066, 0x406A, 0x406E, 0x2C25, 0x4072, 0x4076, - 0x407B, 0x4080, 0x4085, 0x4089, 0x408E, 0x4092, 0x4096, 0x409A, - // Block 9A - 0x409E, 0x2F05, 0x2D71, 0x40A2, 0x40A6, 0x40AA, 0x40AE, 0x40B3, - 0x40B7, 0x40BB, 0x40BF, 0x3016, 0x40C3, 0x40C7, 0x40CC, 0x40D0, - 0x40D4, 0x40D9, 0x40DE, 0x40E2, 0x301A, 0x40E6, 0x40EA, 0x40EE, - 0x40F2, 0x40F6, 0x40FA, 0x40FE, 0x4103, 0x4107, 0x410C, 0x4110, - 0x4115, 0x3022, 0x4119, 0x411D, 0x4122, 0x4126, 0x412A, 0x412F, - 0x4134, 0x4138, 0x413C, 0x4140, 0x4144, 0x4144, 0x4148, 0x414C, - 0x302A, 0x4150, 0x4154, 0x4158, 0x415C, 0x4160, 0x4165, 0x4169, - 0x2BED, 0x416E, 0x4173, 0x4177, 0x417C, 0x4181, 0x4186, 0x418A, - // Block 9B - 0x3042, 0x418E, 0x4193, 0x4198, 0x419D, 0x41A2, 0x41A6, 0x41A6, - 0x3046, 0x30BD, 0x41AA, 0x41AE, 0x41B2, 0x41B6, 0x41BB, 0x2B59, - 0x304E, 0x41BF, 0x41C3, 0x2F2D, 0x41C8, 0x41CD, 0x2E89, 0x41D2, - 0x41D6, 0x2F39, 0x41DA, 0x41DE, 0x41E2, 0x41E7, 0x41E7, 0x41EC, - 0x41F0, 0x41F4, 0x41F9, 0x41FD, 0x4201, 0x4205, 0x420A, 0x420E, - 0x4212, 0x4216, 0x421A, 0x421E, 0x4223, 0x4227, 0x422B, 0x422F, - 0x4233, 0x4237, 0x423B, 0x4240, 0x4245, 0x4249, 0x424E, 0x4252, - 0x4257, 0x425B, 0x2F51, 0x425F, 0x4264, 0x4269, 0x426D, 0x4272, - // Block 9C - 0x4276, 0x427B, 0x427F, 0x4283, 0x4287, 0x428B, 0x428F, 0x4293, - 0x4298, 0x429D, 0x42A2, 0x3F14, 0x42A7, 0x42AB, 0x42AF, 0x42B3, - 0x42B7, 0x42BB, 0x42BF, 0x42C3, 0x42C7, 0x42CB, 0x42CF, 0x42D3, - 0x2C31, 0x42D8, 0x42DC, 0x42E0, 0x42E4, 0x42E8, 0x42EC, 0x2F5D, - 0x42F0, 0x42F4, 0x42F8, 0x42FC, 0x4300, 0x4305, 0x430A, 0x430F, - 0x4313, 0x4317, 0x431B, 0x431F, 0x4324, 0x4328, 0x432D, 0x4331, - 0x4335, 0x433A, 0x433F, 0x4343, 0x2B45, 0x4347, 0x434B, 0x434F, - 0x4353, 0x4357, 0x435B, 0x306A, 0x435F, 0x4363, 0x4367, 0x436B, - // Block 9D - 0x436F, 0x4373, 0x4377, 0x437B, 0x1A66, 0x437F, 0x4384, 0x4388, - 0x438C, 0x4390, 0x4394, 0x4398, 0x439D, 0x43A2, 0x43A6, 0x43AA, - 0x307E, 0x3082, 0x1A82, 0x43AE, 0x43B3, 0x43B7, 0x43BB, 0x43BF, - 0x43C3, 0x43C8, 0x43CD, 0x43D1, 0x43D5, 0x43D9, 0x43DE, 0x3086, - 0x43E2, 0x43E7, 0x43EC, 0x43F0, 0x43F4, 0x43F8, 0x43FD, 0x4401, - 0x4405, 0x4409, 0x440D, 0x4411, 0x4415, 0x4419, 0x441E, 0x4422, - 0x4426, 0x442A, 0x442F, 0x4433, 0x4437, 0x443B, 0x443F, 0x4444, - 0x4449, 0x444D, 0x4451, 0x4455, 0x445A, 0x445E, 0x309E, 0x309E, - // Block 9E - 0x4463, 0x4467, 0x446C, 0x4470, 0x4474, 0x4478, 0x447C, 0x4480, - 0x4484, 0x4488, 0x30A2, 0x448D, 0x4491, 0x4495, 0x4499, 0x449D, - 0x44A1, 0x44A6, 0x44AA, 0x44AF, 0x44B4, 0x1B42, 0x44B9, 0x1B52, - 0x44BD, 0x44C1, 0x44C5, 0x44C9, 0x1B66, 0x44CD, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +var nfkcDecompValues = [10176]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, + // 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, + // 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, + // 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, + // Block 0x7, offset 0x1c0 + 0x01e0: 0x02ca, 0x01e1: 0x02ce, + 0x01ef: 0x02d2, + 0x01f0: 0x02d6, + // 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, + // 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, + // Block 0xa, offset 0x280 + 0x02b0: 0x0494, 0x02b1: 0x0496, 0x02b2: 0x0499, 0x02b3: 0x049b, 0x02b4: 0x049d, 0x02b5: 0x04a0, + 0x02b6: 0x04a3, 0x02b7: 0x04a6, 0x02b8: 0x04a8, + // 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, + // Block 0xc, offset 0x300 + 0x0300: 0x04cc, 0x0301: 0x04cf, 0x0303: 0x04d2, 0x0304: 0x04d5, + 0x0334: 0x04da, + 0x033a: 0x04dd, + 0x033e: 0x04e1, + // 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, + // 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, + // Block 0xf, offset 0x3c0 + 0x03c0: 0x0584, 0x03c1: 0x0589, 0x03c3: 0x058e, + 0x03c7: 0x0593, + 0x03cc: 0x0598, 0x03cd: 0x059d, 0x03ce: 0x05a2, + 0x03d9: 0x05a7, + 0x03f9: 0x05ac, + // Block 0x10, offset 0x400 + 0x0410: 0x05b1, 0x0411: 0x05b6, + 0x0413: 0x05bb, 0x0417: 0x05c0, + 0x041c: 0x05c5, 0x041d: 0x05ca, + 0x041e: 0x05cf, + 0x0436: 0x05d4, 0x0437: 0x05d9, + // 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, + // Block 0x12, offset 0x480 + 0x0487: 0x0688, + // Block 0x13, offset 0x4c0 + 0x04e2: 0x068d, 0x04e3: 0x0692, + 0x04e4: 0x0697, 0x04e5: 0x069c, 0x04e6: 0x06a1, + // Block 0x14, offset 0x500 + 0x0535: 0x06a6, + 0x0536: 0x06ab, 0x0537: 0x06b0, 0x0538: 0x06b5, + // Block 0x15, offset 0x540 + 0x0540: 0x06ba, 0x0542: 0x06bf, + 0x0553: 0x06c4, + // Block 0x16, offset 0x580 + 0x05a9: 0x06c9, + 0x05b1: 0x06d0, 0x05b4: 0x06d7, + // Block 0x17, offset 0x5c0 + 0x05d8: 0x06de, 0x05d9: 0x06e5, 0x05da: 0x06ec, 0x05db: 0x06f3, 0x05dc: 0x06fa, 0x05dd: 0x0701, + 0x05de: 0x0708, 0x05df: 0x070f, + // Block 0x18, offset 0x600 + 0x060b: 0x0716, + 0x060c: 0x071d, + 0x061c: 0x0724, 0x061d: 0x072b, + 0x061f: 0x0732, + // Block 0x19, offset 0x640 + 0x0673: 0x0739, + 0x0676: 0x0740, + // Block 0x1a, offset 0x680 + 0x0699: 0x0747, 0x069a: 0x074e, 0x069b: 0x0755, + 0x069e: 0x075c, + // Block 0x1b, offset 0x6c0 + 0x06c8: 0x0763, 0x06cb: 0x076a, + 0x06cc: 0x0771, + 0x06dc: 0x0778, 0x06dd: 0x077f, + // Block 0x1c, offset 0x700 + 0x0714: 0x0786, + // Block 0x1d, offset 0x740 + 0x074a: 0x078d, 0x074b: 0x0794, + 0x074c: 0x079b, + // Block 0x1e, offset 0x780 + 0x0788: 0x07a2, + // Block 0x1f, offset 0x7c0 + 0x07c0: 0x07a9, + 0x07c7: 0x07b0, 0x07c8: 0x07b7, 0x07ca: 0x07be, 0x07cb: 0x07c5, + // Block 0x20, offset 0x800 + 0x080a: 0x07cf, 0x080b: 0x07d6, + 0x080c: 0x07dd, + // Block 0x21, offset 0x840 + 0x085a: 0x07e4, 0x085c: 0x07eb, 0x085d: 0x07f2, + 0x085e: 0x07fc, + // Block 0x22, offset 0x880 + 0x08b3: 0x0803, + // Block 0x23, offset 0x8c0 + 0x08f3: 0x080a, + // Block 0x24, offset 0x900 + 0x091c: 0x0811, 0x091d: 0x0818, + // Block 0x25, offset 0x940 + 0x094c: 0x081f, + // 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, + // Block 0x27, offset 0x9c0 + 0x09c1: 0x087d, + 0x09d3: 0x0884, + 0x09dd: 0x088b, + 0x09e2: 0x0892, + 0x09e7: 0x0899, + 0x09ec: 0x08a0, + 0x09f9: 0x08a7, + // Block 0x28, offset 0xa00 + 0x0a26: 0x08ae, + // Block 0x29, offset 0xa40 + 0x0a7c: 0x08b5, + // Block 0x2a, offset 0xa80 + 0x0a86: 0x08b9, 0x0a88: 0x08c0, 0x0a8a: 0x08c7, + 0x0a8c: 0x08ce, 0x0a8e: 0x08d5, + 0x0a92: 0x08dc, + 0x0abb: 0x08e3, + 0x0abd: 0x08ea, + // Block 0x2b, offset 0xac0 + 0x0ac0: 0x08f1, 0x0ac1: 0x08f8, 0x0ac3: 0x08ff, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // Block 0x3c, offset 0xf00 + 0x0f09: 0x1599, + 0x0f1a: 0x159f, 0x0f1b: 0x15a5, + 0x0f2e: 0x15ab, + // Block 0x3d, offset 0xf40 + 0x0f4d: 0x15b1, 0x0f4e: 0x15b7, 0x0f4f: 0x15bd, + // Block 0x3e, offset 0xf80 + 0x0f84: 0x15c3, + 0x0f89: 0x15c9, + 0x0f8c: 0x15cf, + 0x0fa4: 0x15d5, 0x0fa6: 0x15db, + 0x0fac: 0x15e1, 0x0fad: 0x15e8, 0x0faf: 0x15f2, + 0x0fb0: 0x15f9, + // 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, + // Block 0x40, offset 0x1000 + 0x1000: 0x1657, 0x1001: 0x165d, 0x1004: 0x1663, 0x1005: 0x1669, + 0x1008: 0x166f, 0x1009: 0x1675, + 0x102c: 0x167b, 0x102d: 0x1681, 0x102e: 0x1687, 0x102f: 0x168d, + // 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, } // nfkcDecompLookup: 960 bytes // Block 0 is the null block. -var nfkcDecompLookup = [...]uint8{ - // Block 0 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 1 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 2 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 3 - 0x00, 0x00, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x00, 0x0A, 0x0B, 0x00, 0x0C, 0x0D, 0x0E, - 0x0F, 0x10, 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, - 0x13, 0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, - 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 4 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x16, 0x17, 0x00, 0x18, - 0x19, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x1C, 0x1D, - 0x00, 0x1E, 0x00, 0x1F, 0x00, 0x20, 0x00, 0x21, - 0x22, 0x00, 0x23, 0x24, 0x25, 0x26, 0x27, 0x00, - // Block 5 - 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2B, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x2E, 0x00, - 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, - // Block 6 - 0x37, 0x38, 0x39, 0x00, 0x3A, 0x3B, 0x3C, 0x3D, - 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x00, 0x00, 0x00, - 0x00, 0x43, 0x44, 0x45, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x47, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, - 0x00, 0x00, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, - // Block 7 - 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x00, - 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 8 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 9 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, - 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, - 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, - // Block A - 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block B - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7F, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block C - 0x00, 0x00, 0x00, 0x00, 0x91, 0x92, 0x93, 0x00, - 0x94, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block D - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, - 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block E - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x0C, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +var nfkcDecompLookup = [960]uint8{ + // Block 0x0, offset 0x0 + // 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, + 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, + // 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, + // 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, + // 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, + // Block 0x8, offset 0x200 + 0x21d: 0x60, + // 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, + // Block 0xa, offset 0x280 + 0x282: 0x7d, + // 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, + // Block 0xc, offset 0x300 + 0x304: 0x91, 0x305: 0x92, 0x306: 0x93, + 0x308: 0x94, 0x309: 0x95, + // Block 0xd, offset 0x340 + 0x360: 0x96, 0x361: 0x97, 0x362: 0x98, 0x363: 0x99, 0x364: 0x9a, 0x365: 0x9b, 0x366: 0x9c, 0x367: 0x9d, + 0x368: 0x9e, + // Block 0xe, offset 0x380 + 0x391: 0x0a, + 0x39d: 0x0b, 0x39f: 0x0c, + 0x3af: 0x0d, } +var nfkcDecompTrie = trie{nfkcDecompLookup[:], nfkcDecompValues[:]} + // recompMap: 7448 bytes (entries only) var recompMap = map[uint32]uint32{ 0x00410300: 0x00C0, @@ -5832,1713 +5358,1223 @@ var recompMap = map[uint32]uint32{ // charInfoValues: 10944 entries, 21888 bytes // Block 2 is the null block. -var charInfoValues = [...]uint16{ - // Block 0 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, 0x8800, 0x8800, 0x0000, - // Block 1 - 0x0000, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, - 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, - 0x8800, 0x0000, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, - 0x8800, 0x8800, 0x8800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, - 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, - 0x8800, 0x0000, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, - 0x8800, 0x8800, 0x8800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3800, 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, - 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, 0x0000, - // Block 4 - 0x1100, 0x1100, 0x9900, 0x1100, 0x9900, 0x9900, 0x8800, 0x9900, - 0x1100, 0x1100, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x9900, - 0x0000, 0x1100, 0x1100, 0x1100, 0x9900, 0x9900, 0x9900, 0x0000, - 0x8800, 0x1100, 0x1100, 0x1100, 0x9900, 0x1100, 0x0000, 0x0000, - 0x1100, 0x1100, 0x9900, 0x1100, 0x9900, 0x9900, 0x8800, 0x9900, - 0x1100, 0x1100, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x9900, - 0x0000, 0x1100, 0x1100, 0x1100, 0x9900, 0x9900, 0x9900, 0x0000, - 0x8800, 0x1100, 0x1100, 0x1100, 0x9900, 0x1100, 0x0000, 0x1100, - // Block 5 - 0x1100, 0x1100, 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x0000, 0x0000, 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x0000, 0x3000, 0x3000, 0x1100, 0x1100, 0x1100, 0x1100, - 0x0000, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x3000, - // Block 6 - 0x3000, 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x3000, 0x0000, 0x0000, 0x9900, 0x9900, 0x1100, 0x1100, - 0x1100, 0x1100, 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, - 0x9900, 0x9900, 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x3800, - // Block 7 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x9900, 0x9900, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9900, - 0x9900, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 8 - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, 0x1100, 0x1100, - 0x1100, 0x1100, 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x3000, 0x3000, 0x3000, 0x1100, 0x1100, 0x0000, 0x0000, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - // Block 9 - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, 0x1100, 0x1100, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9900, 0x9900, - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x9900, 0x9900, - 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block C - 0x66E6, 0x66E6, 0x66E6, 0x66E6, 0x66E6, 0x00E6, 0x66E6, 0x66E6, - 0x66E6, 0x66E6, 0x66E6, 0x66E6, 0x66E6, 0x00E6, 0x00E6, 0x66E6, - 0x00E6, 0x66E6, 0x00E6, 0x66E6, 0x66E6, 0x00E8, 0x00DC, 0x00DC, - 0x00DC, 0x00DC, 0x00E8, 0x66D8, 0x00DC, 0x00DC, 0x00DC, 0x00DC, - 0x00DC, 0x00CA, 0x00CA, 0x66DC, 0x66DC, 0x66DC, 0x66DC, 0x66CA, - 0x66CA, 0x00DC, 0x00DC, 0x00DC, 0x00DC, 0x66DC, 0x66DC, 0x00DC, - 0x66DC, 0x66DC, 0x00DC, 0x00DC, 0x0001, 0x0001, 0x0001, 0x0001, - 0x6601, 0x00DC, 0x00DC, 0x00DC, 0x00DC, 0x00E6, 0x00E6, 0x00E6, - // Block D - 0x33E6, 0x33E6, 0x66E6, 0x33E6, 0x33E6, 0x66F0, 0x00E6, 0x00DC, - 0x00DC, 0x00DC, 0x00E6, 0x00E6, 0x00E6, 0x00DC, 0x00DC, 0x0000, - 0x00E6, 0x00E6, 0x00E6, 0x00DC, 0x00DC, 0x00DC, 0x00DC, 0x00E6, - 0x00E8, 0x00DC, 0x00DC, 0x00E6, 0x00E9, 0x00EA, 0x00EA, 0x00E9, - 0x00EA, 0x00EA, 0x00E9, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, 0x3300, 0x0000, - // Block E - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x3100, 0x1100, 0x3300, - 0x1100, 0x1100, 0x1100, 0x0000, 0x1100, 0x0000, 0x1100, 0x1100, - 0x1100, 0x8800, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x8800, - 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, - 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x0000, - 0x0000, 0x8800, 0x1100, 0x1100, 0x9900, 0x1100, 0x9900, 0x1100, - 0x1100, 0x8800, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x8800, - 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, - // Block F - 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x0000, - 0x0000, 0x8800, 0x9900, 0x9900, 0x1100, 0x1100, 0x9900, 0x0000, - 0x3000, 0x3000, 0x3800, 0x3100, 0x3100, 0x3000, 0x3000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x0000, 0x0000, - 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 10 - 0x1100, 0x1100, 0x0000, 0x1100, 0x0000, 0x0000, 0x8800, 0x1100, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, 0x0000, - 0x8800, 0x0000, 0x0000, 0x8800, 0x0000, 0x8800, 0x8800, 0x8800, - 0x8800, 0x1100, 0x8800, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, - 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, 0x8800, - 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x8800, 0x0000, 0x0000, - 0x8800, 0x0000, 0x0000, 0x8800, 0x0000, 0x8800, 0x8800, 0x8800, - 0x8800, 0x1100, 0x8800, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, - // Block 11 - 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, 0x8800, - 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x8800, 0x0000, 0x0000, - 0x1100, 0x1100, 0x0000, 0x1100, 0x0000, 0x0000, 0x8800, 0x1100, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, 0x8800, 0x1100, 0x1100, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 12 - 0x0000, 0x0000, 0x0000, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 13 - 0x0000, 0x1100, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, 0x1100, 0x1100, - 0x8800, 0x8800, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x8800, 0x8800, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, - 0x1100, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 14 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x00DC, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00DC, 0x00E6, - 0x00E6, 0x00E6, 0x00DE, 0x00DC, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00DC, 0x00DC, 0x00DC, 0x00DC, 0x00DC, 0x00DC, - 0x00E6, 0x00E6, 0x00DC, 0x00E6, 0x00E6, 0x00DE, 0x00E4, 0x00E6, - 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, - 0x0012, 0x0013, 0x0013, 0x0014, 0x0015, 0x0016, 0x0000, 0x0017, - // Block 15 - 0x0000, 0x0018, 0x0019, 0x0000, 0x00E6, 0x00DC, 0x0000, 0x0012, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 16 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x001E, 0x001F, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x8800, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 17 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x8800, 0x0000, 0x8800, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, - 0x0020, 0x0021, 0x0022, 0x66E6, 0x66E6, 0x66DC, 0x00DC, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00DC, 0x00E6, 0x00E6, 0x00DC, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0023, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 18 - 0x1100, 0x8800, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x8800, 0x1100, 0x0000, 0x8800, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x0000, 0x0000, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00DC, 0x00E6, 0x0000, 0x0000, 0x00E6, - 0x00E6, 0x0000, 0x00DC, 0x00E6, 0x00E6, 0x00DC, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 19 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0024, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00E6, 0x00DC, 0x00E6, 0x00E6, 0x00DC, 0x00E6, 0x00E6, 0x00DC, - 0x00DC, 0x00DC, 0x00E6, 0x00DC, 0x00DC, 0x00E6, 0x00DC, 0x00E6, - // Block 1A - 0x00E6, 0x00E6, 0x00DC, 0x00E6, 0x00DC, 0x00E6, 0x00DC, 0x00E6, - 0x00DC, 0x00E6, 0x00E6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00DC, 0x00E6, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x0000, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x0000, 0x00E6, 0x00E6, 0x00E6, - 0x0000, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x00DC, 0x00DC, 0x00DC, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 1E - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x8800, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x8800, 0x1100, 0x0000, 0x8800, 0x1100, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x6607, 0x0000, 0x0000, 0x0000, - // Block 1F - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, - 0x0000, 0x00E6, 0x00DC, 0x00E6, 0x00E6, 0x0000, 0x0000, 0x0000, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 20 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0x0000, 0x6600, 0x0000, - // Block 21 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, - 0x0000, 0x0000, 0x0000, 0x1100, 0x1100, 0x0009, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, 0x3300, 0x0000, 0x3300, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 22 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, 0x3300, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0x0000, 0x0000, 0x0000, - // Block 23 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x3300, 0x3300, 0x3300, 0x0000, 0x0000, 0x3300, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 24 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0x0000, 0x0000, 0x0000, - // Block 25 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 26 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, - 0x1100, 0x0000, 0x0000, 0x1100, 0x1100, 0x0009, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x6600, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, 0x3300, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 27 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x8800, 0x0000, 0x1100, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x0000, - // Block 28 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, 0x8800, - 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, 0x0009, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 29 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, - 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0054, 0x665B, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0x0000, 0x0000, 0x8800, - // Block 2B - 0x1100, 0x0000, 0x6600, 0x0000, 0x0000, 0x0000, 0x8800, 0x1100, - 0x1100, 0x0000, 0x9900, 0x1100, 0x0000, 0x0009, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x6600, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x0000, - // Block 2D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x6609, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x8800, 0x1100, 0x0000, 0x9900, 0x1100, 0x1100, 0x6600, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2E - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0067, 0x0067, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 2F - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x006B, 0x006B, 0x006B, 0x006B, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 30 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0076, 0x0076, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 31 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x007A, 0x007A, 0x007A, 0x007A, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 32 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00DC, 0x00DC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00DC, 0x0000, 0x00DC, - 0x0000, 0x00D8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 33 - 0x0000, 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, - 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x3300, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0081, 0x0082, 0x3300, 0x0084, 0x3300, 0x3300, 0x3000, - 0x3300, 0x3000, 0x0082, 0x0082, 0x0082, 0x0082, 0x0000, 0x0000, - // Block 34 - 0x0082, 0x3300, 0x00E6, 0x00E6, 0x0009, 0x0000, 0x00E6, 0x00E6, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, - 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x3300, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 35 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00DC, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 36 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, 0x1100, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, - 0x0000, 0x0009, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 37 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00DC, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 38 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, - // Block 39 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, - 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, - 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, - 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, - 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, - // Block 3B - 0x6600, 0x6600, 0x6600, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00E6, 0x00E6, 0x00E6, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3E - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00E6, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 3F - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x00E4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 40 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x00DE, 0x00E6, 0x00DC, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 41 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00E6, - 0x00DC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 42 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x0000, 0x0000, 0x00DC, - // Block 43 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, 0x1100, 0x8800, - 0x1100, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, 0x1100, 0x0000, - 0x0000, 0x8800, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0x6600, 0x0000, 0x0000, - 0x0000, 0x0000, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, 0x8800, - // Block 44 - 0x1100, 0x1100, 0x8800, 0x1100, 0x0009, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x00E6, 0x00DC, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 45 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 46 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0009, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 47 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 48 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00E6, 0x00E6, 0x00E6, 0x0000, 0x0001, 0x00DC, 0x00DC, 0x00DC, - 0x00DC, 0x00DC, 0x00E6, 0x00E6, 0x00DC, 0x00DC, 0x00DC, 0x00DC, - 0x00E6, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x00DC, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 49 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 4A - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 4B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 4C - 0x00E6, 0x00E6, 0x00DC, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00DC, 0x00E6, 0x00E6, 0x00EA, 0x00D6, 0x00DC, - 0x00CA, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x00E9, 0x00DC, 0x00E6, 0x00DC, - // Block 4D - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x9900, 0x9900, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - // Block 4E - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - // Block 4F - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x3000, 0x3100, 0x0000, 0x0000, 0x0000, 0x0000, - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - // Block 50 - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x9900, 0x9900, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 51 - 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, - 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, - 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, - 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - // Block 52 - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, - 0x9900, 0x9900, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x0000, 0x9900, 0x0000, 0x1100, 0x0000, 0x1100, 0x0000, 0x1100, - 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, - 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, - 0x9900, 0x3300, 0x1100, 0x3300, 0x9900, 0x3300, 0x1100, 0x3300, - 0x1100, 0x3300, 0x1100, 0x3300, 0x9900, 0x3300, 0x0000, 0x0000, - // Block 53 - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x9900, 0x1100, - 0x1100, 0x1100, 0x1100, 0x3300, 0x1100, 0x3000, 0x3300, 0x3800, - // Block 54 - 0x3000, 0x3100, 0x1100, 0x1100, 0x1100, 0x0000, 0x9900, 0x1100, - 0x1100, 0x3300, 0x1100, 0x3300, 0x1100, 0x3100, 0x3100, 0x3100, - 0x1100, 0x1100, 0x1100, 0x3300, 0x0000, 0x0000, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x3300, 0x0000, 0x3100, 0x3100, 0x3100, - 0x1100, 0x1100, 0x1100, 0x3300, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x3300, 0x1100, 0x3100, 0x3300, 0x3300, - 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, 0x0000, 0x9900, 0x1100, - 0x1100, 0x3300, 0x1100, 0x3300, 0x1100, 0x3300, 0x3800, 0x0000, - // Block 55 - 0x3300, 0x3300, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x0000, 0x3000, 0x0000, - // Block 56 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, - 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 57 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 58 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00E6, 0x00E6, 0x0001, 0x0001, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x0001, 0x0001, 0x0001, 0x00E6, 0x00E6, 0x0000, 0x0000, 0x0000, - 0x0000, 0x00E6, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x00E6, - 0x00DC, 0x00E6, 0x0001, 0x0001, 0x00DC, 0x00DC, 0x00DC, 0x00DC, - 0x00E6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 59 - 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, - 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x0000, - 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x0000, 0x3300, 0x0000, - 0x3000, 0x0000, 0x3300, 0x3300, 0x3000, 0x3000, 0x0000, 0x3000, - 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 5A - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 5B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x8800, 0x0000, 0x8800, 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x1100, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1100, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 5C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, - 0x8800, 0x0000, 0x8800, 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 5D - 0x0000, 0x0000, 0x0000, 0x8800, 0x1100, 0x0000, 0x0000, 0x0000, - 0x8800, 0x1100, 0x0000, 0x8800, 0x1100, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x8800, 0x1100, 0x8800, 0x1100, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x0000, 0x3000, - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, - // Block 5E - 0x0000, 0x1100, 0x0000, 0x8800, 0x1100, 0x8800, 0x0000, 0x1100, - 0x8800, 0x1100, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1100, 0x8800, 0x1100, 0x0000, 0x8800, 0x8800, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x8800, 0x8800, 0x1100, 0x1100, 0x8800, 0x8800, - 0x1100, 0x1100, 0x8800, 0x8800, 0x8800, 0x8800, 0x0000, 0x0000, - // Block 5F - 0x1100, 0x1100, 0x8800, 0x8800, 0x1100, 0x1100, 0x8800, 0x8800, - 0x1100, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x8800, 0x8800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x8800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x8800, 0x8800, 0x0000, 0x8800, 0x1100, 0x1100, 0x1100, 0x1100, - 0x0000, 0x0000, 0x8800, 0x8800, 0x8800, 0x8800, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 60 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 61 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x3300, 0x3300, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 62 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 63 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 64 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 65 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 66 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 67 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 68 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x0000, 0x0000, - // Block 69 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00E6, - 0x00E6, 0x00E6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 6A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, - // Block 6B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - // Block 6C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 6D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 6E - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 6F - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x00DA, 0x00E4, 0x00E8, 0x00DE, 0x00E0, 0x00E0, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 70 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, - 0x0000, 0x0000, 0x0000, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, - 0x1100, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, - 0x1100, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, - 0x1100, 0x8800, 0x1100, 0x0000, 0x8800, 0x1100, 0x8800, 0x1100, - 0x8800, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, - 0x1100, 0x1100, 0x8800, 0x1100, 0x1100, 0x8800, 0x1100, 0x1100, - 0x8800, 0x1100, 0x1100, 0x8800, 0x1100, 0x1100, 0x0000, 0x0000, - // Block 71 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x1100, 0x0000, 0x0000, 0x0000, - 0x0000, 0x6608, 0x6608, 0x3000, 0x3000, 0x8800, 0x1100, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, - 0x0000, 0x0000, 0x0000, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, - 0x1100, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, - 0x1100, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, 0x1100, 0x8800, - // Block 72 - 0x1100, 0x8800, 0x1100, 0x0000, 0x8800, 0x1100, 0x8800, 0x1100, - 0x8800, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, - 0x1100, 0x1100, 0x8800, 0x1100, 0x1100, 0x8800, 0x1100, 0x1100, - 0x8800, 0x1100, 0x1100, 0x8800, 0x1100, 0x1100, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, - 0x8800, 0x8800, 0x8800, 0x0000, 0x1100, 0x0000, 0x0000, 0x1100, - 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, 0x8800, 0x1100, 0x3000, - // Block 73 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 74 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 75 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 76 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - // Block 77 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - // Block 78 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00E6, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x00E6, 0x00E6, 0x0000, 0x0000, - // Block 79 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00E6, 0x00E6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 7A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 7B - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 7C - 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 7D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x00DC, 0x00DC, 0x00DC, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 7E - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 7F - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0007, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 80 - 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 81 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00E6, 0x0000, 0x00E6, 0x00E6, 0x00DC, 0x0000, 0x0000, 0x00E6, - 0x00E6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00E6, 0x00E6, - // Block 82 - 0x0000, 0x00E6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 83 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 84 - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - // Block 85 - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, 0x1100, - 0x1100, 0x1100, 0x1100, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 86 - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - // Block 87 - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x0000, 0x0000, - 0x3300, 0x0000, 0x3300, 0x0000, 0x0000, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x0000, - 0x3300, 0x0000, 0x3300, 0x0000, 0x0000, 0x3300, 0x3300, 0x0000, - 0x0000, 0x0000, 0x3300, 0x3300, 0x3300, 0x3300, 0x0000, 0x0000, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - // Block 88 - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x0000, 0x0000, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - // Block 89 - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 8A - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, 0x001A, 0x3300, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x0000, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x0000, 0x3300, 0x0000, - // Block 8B - 0x3300, 0x3300, 0x0000, 0x3300, 0x3300, 0x0000, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 8C - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 8D - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 8E - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, - // Block 8F - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 90 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 91 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, - // Block 92 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 93 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x0000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 94 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, - // Block 95 - 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 96 - 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 97 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00DC, 0x0000, 0x0000, - // Block 98 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00DC, 0x0000, 0x00E6, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00E6, 0x0001, 0x00DC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, - // Block 99 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x8800, 0x1100, 0x8800, 0x1100, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8800, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0009, 0x6607, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 9A - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x00D8, 0x00D8, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x00E2, 0x00D8, 0x00D8, - 0x00D8, 0x00D8, 0x00D8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x00DC, 0x00DC, 0x00DC, 0x00DC, 0x00DC, - // Block 9B - 0x00DC, 0x00DC, 0x00DC, 0x0000, 0x0000, 0x00E6, 0x00E6, 0x00E6, - 0x00E6, 0x00E6, 0x00DC, 0x00DC, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - // Block 9C - 0x3300, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 9D - 0x0000, 0x0000, 0x00E6, 0x00E6, 0x00E6, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block 9E - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block 9F - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x3000, 0x0000, 0x0000, 0x3000, 0x3000, 0x0000, - 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x0000, 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, - // Block A0 - 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block A1 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - // Block A2 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x0000, - 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block A3 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block A4 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block A5 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - // Block A6 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block A7 - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block A8 - 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block A9 - 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, - 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - // Block AA - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, - 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x3300, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +var charInfoValues = [10944]uint16{ + // Block 0x0, offset 0x0 + 0x003c: 0x8800, 0x003d: 0x8800, 0x003e: 0x8800, + // Block 0x1, offset 0x40 + 0x0041: 0x8800, 0x0042: 0x8800, 0x0043: 0x8800, 0x0044: 0x8800, 0x0045: 0x8800, + 0x0046: 0x8800, 0x0047: 0x8800, 0x0048: 0x8800, 0x0049: 0x8800, 0x004a: 0x8800, 0x004b: 0x8800, + 0x004c: 0x8800, 0x004d: 0x8800, 0x004e: 0x8800, 0x004f: 0x8800, 0x0050: 0x8800, + 0x0052: 0x8800, 0x0053: 0x8800, 0x0054: 0x8800, 0x0055: 0x8800, 0x0056: 0x8800, 0x0057: 0x8800, + 0x0058: 0x8800, 0x0059: 0x8800, 0x005a: 0x8800, + 0x0061: 0x8800, 0x0062: 0x8800, 0x0063: 0x8800, + 0x0064: 0x8800, 0x0065: 0x8800, 0x0066: 0x8800, 0x0067: 0x8800, 0x0068: 0x8800, 0x0069: 0x8800, + 0x006a: 0x8800, 0x006b: 0x8800, 0x006c: 0x8800, 0x006d: 0x8800, 0x006e: 0x8800, 0x006f: 0x8800, + 0x0070: 0x8800, 0x0072: 0x8800, 0x0073: 0x8800, 0x0074: 0x8800, 0x0075: 0x8800, + 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, + // 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, + // 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, + // 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, + // Block 0x7, offset 0x1c0 + 0x01e0: 0x9900, 0x01e1: 0x9900, + 0x01ef: 0x9900, + 0x01f0: 0x9900, + 0x01f7: 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, + // 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, + // Block 0xa, offset 0x280 + 0x0292: 0x8800, + 0x02b0: 0x3000, 0x02b1: 0x3000, 0x02b2: 0x3000, 0x02b3: 0x3000, 0x02b4: 0x3000, 0x02b5: 0x3000, + 0x02b6: 0x3000, 0x02b7: 0x3000, 0x02b8: 0x3000, + // 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, + // 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, + // 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, + // 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, + // 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, } // charInfoLookup: 1152 bytes // Block 0 is the null block. -var charInfoLookup = [...]uint8{ - // Block 0 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 1 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 2 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 3 - 0x00, 0x00, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x00, 0x00, 0x14, 0x15, - 0x16, 0x17, 0x00, 0x18, 0x19, 0x1A, 0x00, 0x1B, - 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x09, 0x09, 0x0A, 0x00, 0x0B, - 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 4 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1C, 0x1D, 0x00, 0x00, 0x1E, 0x1F, 0x20, 0x21, - 0x22, 0x23, 0x24, 0x25, 0x20, 0x26, 0x27, 0x28, - 0x00, 0x29, 0x2A, 0x2B, 0x2C, 0x28, 0x00, 0x2D, - 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, - // Block 5 - 0x36, 0x00, 0x37, 0x38, 0x00, 0x39, 0x3A, 0x3B, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x3E, - 0x00, 0x00, 0x3F, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x41, 0x42, 0x00, 0x00, 0x43, 0x44, 0x45, 0x46, - 0x47, 0x00, 0x00, 0x48, 0x49, 0x4A, 0x4B, 0x4C, - 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, - // Block 6 - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, - 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x00, 0x00, 0x00, - 0x00, 0x62, 0x63, 0x64, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x65, 0x66, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x68, 0x00, 0x69, 0x00, 0x6A, 0x00, 0x6B, - 0x00, 0x00, 0x6C, 0x6D, 0x63, 0x63, 0x63, 0x6E, - // Block 7 - 0x6F, 0x70, 0x71, 0x72, 0x73, 0x63, 0x74, 0x00, - 0x75, 0x76, 0x63, 0x77, 0x63, 0x63, 0x63, 0x63, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 8 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x00, 0x79, 0x00, 0x7A, 0x00, 0x00, - 0x7B, 0x00, 0x00, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, - 0x00, 0x00, 0x81, 0x82, 0x00, 0x00, 0x00, 0x83, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - // Block 9 - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - // Block A - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x85, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block B - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x86, 0x86, 0x86, 0x86, - 0x87, 0x88, 0x86, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, - 0x63, 0x63, 0x63, 0x63, 0x8E, 0x8F, 0x90, 0x91, - 0x92, 0x93, 0x63, 0x94, 0x95, 0x63, 0x77, 0x96, - // Block C - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block D - 0x00, 0x7B, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block E - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x9B, 0x9C, - 0x00, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x63, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0x63, 0x63, - 0x63, 0x63, 0xA3, 0x63, 0x63, 0x63, 0x63, 0xA4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block F - 0x00, 0x00, 0x00, 0x00, 0xA5, 0xA6, 0xA7, 0x00, - 0xA8, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 10 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Block 11 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0C, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x0F, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +var charInfoLookup = [1152]uint8{ + // Block 0x0, offset 0x0 + // 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, + 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // 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, + // Block 0xc, offset 0x300 + 0x307: 0x97, + 0x328: 0x98, + // Block 0xd, offset 0x340 + 0x341: 0x7b, 0x342: 0x99, + // 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, + // Block 0xf, offset 0x3c0 + 0x3c4: 0xa5, 0x3c5: 0xa6, 0x3c6: 0xa7, + 0x3c8: 0xa8, 0x3c9: 0xa9, + // Block 0x10, offset 0x400 + 0x420: 0x86, 0x421: 0x86, 0x422: 0x86, 0x423: 0x86, 0x424: 0x86, 0x425: 0x86, 0x426: 0x86, 0x427: 0x86, + 0x428: 0xaa, + // Block 0x11, offset 0x440 + 0x450: 0x0c, 0x451: 0x0d, + 0x45d: 0x0e, 0x45f: 0x0f, + 0x46f: 0x10, } +var charInfoTrie = trie{charInfoLookup[:], charInfoValues[:]} + // Total size of tables: 78KB (80234 bytes) diff --git a/src/pkg/exp/norm/trie.go b/src/pkg/exp/norm/trie.go new file mode 100644 index 000000000..6b6540187 --- /dev/null +++ b/src/pkg/exp/norm/trie.go @@ -0,0 +1,234 @@ +// 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 + +type trie struct { + index []uint8 + values []uint16 +} + +const ( + t1 = 0x00 // 0000 0000 + tx = 0x80 // 1000 0000 + t2 = 0xC0 // 1100 0000 + t3 = 0xE0 // 1110 0000 + t4 = 0xF0 // 1111 0000 + t5 = 0xF8 // 1111 1000 + t6 = 0xFC // 1111 1100 + te = 0xFE // 1111 1110 + + maskx = 0x3F // 0011 1111 + mask2 = 0x1F // 0001 1111 + mask3 = 0x0F // 0000 1111 + mask4 = 0x07 // 0000 0111 +) + +// lookup returns the trie value for the first UTF-8 encoding in s and +// the width in bytes of this encoding. The size will be 0 if s does not +// hold enough bytes to complete the encoding. len(s) must be greater than 0. +func (t *trie) lookup(s []byte) (v uint16, sz int) { + c0 := s[0] + switch { + case c0 < tx: + return t.values[c0], 1 + case c0 < t2: + return 0, 1 + case c0 < t3: + if len(s) < 2 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + o := uint16(i)<<6 + uint16(c1)&maskx + return t.values[o], 2 + case c0 < t4: + if len(s) < 3 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + o := uint16(i)<<6 + uint16(c1)&maskx + i = t.index[o] + c2 := s[2] + if c2 < tx || t2 <= c2 { + return 0, 2 + } + o = uint16(i)<<6 + uint16(c2)&maskx + return t.values[o], 3 + case c0 < t5: + if len(s) < 4 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + o := uint16(i)<<6 + uint16(c1)&maskx + i = t.index[o] + c2 := s[2] + if c2 < tx || t2 <= c2 { + return 0, 2 + } + o = uint16(i)<<6 + uint16(c2)&maskx + i = t.index[o] + c3 := s[3] + 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 + } + // Illegal rune + return 0, 1 +} + +// lookupString returns the trie value for the first UTF-8 encoding in s and +// the width in bytes of this encoding. The size will be 0 if s does not +// hold enough bytes to complete the encoding. len(s) must be greater than 0. +func (t *trie) lookupString(s string) (v uint16, sz int) { + c0 := s[0] + switch { + case c0 < tx: + return t.values[c0], 1 + case c0 < t2: + return 0, 1 + case c0 < t3: + if len(s) < 2 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + o := uint16(i)<<6 + uint16(c1)&maskx + return t.values[o], 2 + case c0 < t4: + if len(s) < 3 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + o := uint16(i)<<6 + uint16(c1)&maskx + i = t.index[o] + c2 := s[2] + if c2 < tx || t2 <= c2 { + return 0, 2 + } + o = uint16(i)<<6 + uint16(c2)&maskx + return t.values[o], 3 + case c0 < t5: + if len(s) < 4 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + o := uint16(i)<<6 + uint16(c1)&maskx + i = t.index[o] + c2 := s[2] + if c2 < tx || t2 <= c2 { + return 0, 2 + } + o = uint16(i)<<6 + uint16(c2)&maskx + i = t.index[o] + c3 := s[3] + 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 + } + // Illegal rune + return 0, 1 +} + +// lookupUnsafe returns the trie value for the first UTF-8 encoding in s. +// s must hold a full encoding. +func (t *trie) lookupUnsafe(s []byte) uint16 { + c0 := s[0] + if c0 < tx { + return t.values[c0] + } + if c0 < t2 { + return 0 + } + i := t.index[c0] + o := uint16(i)<<6 + uint16(s[1])&maskx + if c0 < t3 { + return t.values[o] + } + i = t.index[o] + o = uint16(i)<<6 + uint16(s[2])&maskx + if c0 < t4 { + return t.values[o] + } + i = t.index[o] + o = uint16(i)<<6 + uint16(s[3])&maskx + if c0 < t5 { + return t.values[o] + } + return 0 +} + +// lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s. +// s must hold a full encoding. +func (t *trie) lookupStringUnsafe(s string) uint16 { + c0 := s[0] + if c0 < tx { + return t.values[c0] + } + if c0 < t2 { + return 0 + } + i := t.index[c0] + o := uint16(i)<<6 + uint16(s[1])&maskx + if c0 < t3 { + return t.values[o] + } + i = t.index[o] + o = uint16(i)<<6 + uint16(s[2])&maskx + if c0 < t4 { + return t.values[o] + } + i = t.index[o] + o = uint16(i)<<6 + uint16(s[3])&maskx + if c0 < t5 { + return t.values[o] + } + return 0 +} diff --git a/src/pkg/exp/norm/trie_test.go b/src/pkg/exp/norm/trie_test.go new file mode 100644 index 000000000..ad87d972b --- /dev/null +++ b/src/pkg/exp/norm/trie_test.go @@ -0,0 +1,107 @@ +package norm + +import ( + "testing" + "utf8" +) + +// Test data is located in triedata_test.go; generated by maketesttables. +var testdata = testdataTrie + +// Test cases for illegal runes. +type trietest struct { + size int + bytes []byte +} + +var tests = []trietest{ + // illegal runes + {1, []byte{0x80}}, + {1, []byte{0xFF}}, + {1, []byte{t2, tx - 1}}, + {1, []byte{t2, t2}}, + {2, []byte{t3, tx, tx - 1}}, + {2, []byte{t3, tx, t2}}, + {1, []byte{t3, tx - 1, tx}}, + {3, []byte{t4, tx, tx, tx - 1}}, + {3, []byte{t4, tx, tx, t2}}, + {1, []byte{t4, t2, tx, tx - 1}}, + {2, []byte{t4, tx, t2, tx - 1}}, + + // short runes + {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}}, +} + +func mkUtf8(rune int) ([]byte, int) { + var b [utf8.UTFMax]byte + sz := utf8.EncodeRune(b[:], rune) + return b[:sz], sz +} + +func TestLookup(t *testing.T) { + for i, tt := range testRunes { + 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) + } + if szt != szg { + t.Errorf("lookup(%U): found size %d, expected %d", i, szt, szg) + } + } + for i, tt := range tests { + v, sz := testdata.lookup(tt.bytes) + if int(v) != 0 { + t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v) + } + if sz != tt.size { + t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size) + } + } +} + +func TestLookupUnsafe(t *testing.T) { + for i, tt := range testRunes { + b, _ := mkUtf8(tt) + v := testdata.lookupUnsafe(b) + if int(v) != i { + t.Errorf("lookupUnsafe(%U): found value %#x, expected %#x", i, v, i) + } + } +} + +func TestLookupString(t *testing.T) { + for i, tt := range testRunes { + b, szg := mkUtf8(tt) + v, szt := testdata.lookupString(string(b)) + if int(v) != i { + t.Errorf("lookup(%U): found value %#x, expected %#x", i, v, i) + } + if szt != szg { + t.Errorf("lookup(%U): found size %d, expected %d", i, szt, szg) + } + } + for i, tt := range tests { + v, sz := testdata.lookupString(string(tt.bytes)) + if int(v) != 0 { + t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v) + } + if sz != tt.size { + t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size) + } + } +} + +func TestLookupStringUnsafe(t *testing.T) { + for i, tt := range testRunes { + b, _ := mkUtf8(tt) + v := testdata.lookupStringUnsafe(string(b)) + if int(v) != i { + t.Errorf("lookupUnsafe(%U): found value %#x, expected %#x", i, v, i) + } + } +} diff --git a/src/pkg/exp/norm/triedata_test.go b/src/pkg/exp/norm/triedata_test.go new file mode 100644 index 000000000..f886e6004 --- /dev/null +++ b/src/pkg/exp/norm/triedata_test.go @@ -0,0 +1,63 @@ +// Generated by running +// maketesttables +// DO NOT EDIT + +package norm + +var testRunes = []int{1, 12, 127, 128, 256, 2047, 2048, 2457, 65535, 65536, 65793, 1114111} + +// testdataValues: 768 entries, 1536 bytes +// Block 2 is the null block. +var testdataValues = [768]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, +} + +// testdataLookup: 640 bytes +// Block 0 is the null block. +var testdataLookup = [640]uint8{ + // Block 0x0, offset 0x0 + // Block 0x1, offset 0x40 + // Block 0x2, offset 0x80 + // Block 0x3, offset 0xc0 + 0x0c2: 0x03, 0x0c4: 0x04, + 0x0df: 0x05, + 0x0e0: 0x04, + 0x0ef: 0x05, + 0x0f0: 0x07, 0x0f4: 0x09, + // Block 0x4, offset 0x100 + 0x120: 0x06, 0x126: 0x07, + // Block 0x5, offset 0x140 + 0x17f: 0x08, + // Block 0x6, offset 0x180 + 0x180: 0x09, 0x184: 0x0a, + // Block 0x7, offset 0x1c0 + 0x1d0: 0x06, + // Block 0x8, offset 0x200 + 0x23f: 0x0b, + // Block 0x9, offset 0x240 + 0x24f: 0x08, +} + +var testdataTrie = trie{testdataLookup[:], testdataValues[:]} diff --git a/src/pkg/exp/norm/triegen.go b/src/pkg/exp/norm/triegen.go new file mode 100644 index 000000000..2b7eeee17 --- /dev/null +++ b/src/pkg/exp/norm/triegen.go @@ -0,0 +1,211 @@ +// 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. + +// Trie table generator. +// Used by make*tables tools to generate a go file with trie data structures +// for mapping UTF-8 to a 16-bit value. All but the last byte in a UTF-8 byte +// sequence are used to lookup offsets in the index table to be used for the +// next byte. The last byte is used to index into a table with 16-bit values. + +package main + +import ( + "fmt" + "hash/crc32" + "log" + "utf8" +) + +// Intermediate trie structure +type trieNode struct { + table [256]*trieNode + value uint16 + b byte + leaf bool +} + +func newNode() *trieNode { + return new(trieNode) +} + +func (n trieNode) String() string { + s := fmt.Sprint("trieNode{table: { non-nil at index: ") + for i, v := range n.table { + if v != nil { + s += fmt.Sprintf("%d, ", i) + } + } + s += fmt.Sprintf("}, value:%#x, b:%#x leaf:%v}", n.value, n.b, n.leaf) + return s +} + +func (n trieNode) isInternal() bool { + internal := true + for i := 0; i < 256; i++ { + if nn := n.table[i]; nn != nil { + if !internal && !nn.leaf { + log.Fatalf("triegen: isInternal: node contains both leaf and non-leaf children (%v)", n) + } + internal = internal && !nn.leaf + } + } + return internal +} + +func (n *trieNode) insert(rune int, value uint16) { + var p [utf8.UTFMax]byte + sz := utf8.EncodeRune(p[:], rune) + + for i := 0; i < sz; i++ { + if n.leaf { + log.Fatalf("triegen: insert: node (%#v) should not be a leaf", n) + } + nn := n.table[p[i]] + if nn == nil { + nn = newNode() + nn.b = p[i] + n.table[p[i]] = nn + } + n = nn + } + n.value = value + n.leaf = true +} + +type nodeIndex struct { + lookupBlocks []*trieNode + valueBlocks []*trieNode + + lookupBlockIdx map[uint32]uint16 + valueBlockIdx map[uint32]uint16 +} + +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) + return index +} + +func computeOffsets(index *nodeIndex, n *trieNode) uint16 { + 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 + if nn := n.table[0x80+i]; nn != nil { + v = computeOffsets(index, nn) + } + hasher.Write([]byte{uint8(v >> 8), uint8(v)}) + } + h := hasher.Sum32() + if n.isInternal() { + v, ok := index.lookupBlockIdx[h] + if !ok { + v = uint16(len(index.lookupBlocks)) + index.lookupBlocks = append(index.lookupBlocks, n) + index.lookupBlockIdx[h] = v + } + n.value = v + } else { + v, ok := index.valueBlockIdx[h] + if !ok { + v = uint16(len(index.valueBlocks)) + index.valueBlocks = append(index.valueBlocks, n) + index.valueBlockIdx[h] = v + } + n.value = v + } + return n.value +} + +func printValueBlock(nr int, n *trieNode, offset int) { + boff := nr * 64 + fmt.Printf("\n// Block %#x, offset %#x", nr, boff) + var printnewline bool + for i := 0; i < 64; i++ { + if i%6 == 0 { + printnewline = true + } + v := uint16(0) + if nn := n.table[i+offset]; nn != nil { + v = nn.value + } + if v != 0 { + if printnewline { + fmt.Printf("\n") + printnewline = false + } + fmt.Printf("%#04x:%#04x, ", nr*64+i, v) + } + } +} + +func printLookupBlock(nr int, n *trieNode, offset int) { + boff := nr * 64 + fmt.Printf("\n// Block %#x, offset %#x", nr, boff) + var printnewline bool + for i := 0; i < 64; i++ { + if i%8 == 0 { + printnewline = true + } + v := uint16(0) + if nn := n.table[i+offset]; nn != nil { + v = nn.value + } + if v != 0 { + if printnewline { + fmt.Printf("\n") + printnewline = false + } + fmt.Printf("%#03x:%#02x, ", boff+i, v) + } + } +} + +// printTables returns the size in bytes of the generated tables. +func (t *trieNode) printTables(name string) int { + index := newIndex() + // Values for 7-bit ASCII are stored in first two block, followed by nil block. + index.valueBlocks = append(index.valueBlocks, nil, nil, nil) + // First byte of multi-byte UTF-8 codepoints are indexed in 4th block. + index.lookupBlocks = append(index.lookupBlocks, nil, nil, nil, nil) + // Index starter bytes of multi-byte UTF-8. + for i := 0xC0; i < 0x100; i++ { + if t.table[i] != nil { + computeOffsets(index, t.table[i]) + } + } + + nv := len(index.valueBlocks) * 64 + 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) + printValueBlock(0, t, 0) + printValueBlock(1, t, 64) + printValueBlock(2, newNode(), 0) + for i := 3; i < len(index.valueBlocks); i++ { + printValueBlock(i, index.valueBlocks[i], 0x80) + } + fmt.Print("\n}\n\n") + + ni := len(index.lookupBlocks) * 64 + 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) + for i := 4; i < len(index.lookupBlocks); i++ { + printLookupBlock(i, index.lookupBlocks[i], 0x80) + } + fmt.Print("\n}\n\n") + fmt.Printf("var %sTrie = trie{ %sLookup[:], %sValues[:] }\n\n", name, name, name) + return nv*2 + ni +} diff --git a/src/pkg/exp/template/Makefile b/src/pkg/exp/template/Makefile deleted file mode 100644 index 06df9b659..000000000 --- a/src/pkg/exp/template/Makefile +++ /dev/null @@ -1,15 +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. - -include ../../../Make.inc - -TARG=exp/template -GOFILES=\ - exec.go\ - funcs.go\ - helper.go\ - parse.go\ - set.go\ - -include ../../../Make.pkg diff --git a/src/pkg/exp/template/doc.go b/src/pkg/exp/template/doc.go deleted file mode 100644 index ef9e1563b..000000000 --- a/src/pkg/exp/template/doc.go +++ /dev/null @@ -1,300 +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 template implements data-driven templates for generating textual output -such as HTML. - -Templates are executed by applying them to a data structure. Annotations in the -template refer to elements of the data structure (typically a field of a struct -or a key in a map) to control execution and derive values to be displayed. -Execution of the template walks the structure and sets the cursor, represented -by a period '.' and called "dot", to the value at the current location in the -structure as execution proceeds. - -The input text for a template is UTF-8-encoded text in any format. -"Actions"--data evaluations or control structures--are delimited by -"{{" and "}}"; all text outside actions is copied to the output unchanged. -Actions may not span newlines, although comments can. - -Once constructed, templates and template sets can be executed safely in -parallel. - -Actions - -Here is the list of actions. "Arguments" and "pipelines" are evaluations of -data, defined in detail below. - -*/ -// {{/* a comment */}} -// A comment; discarded. May contain newlines. -// Comments do not nest. -/* - - {{pipeline}} - The default textual representation of the value of the pipeline - is copied to the output. - - {{if pipeline}} T1 {{end}} - If the value of the pipeline is empty, no output is generated; - otherwise, T1 is executed. The empty values are false, 0, any - nil pointer or interface value, and any array, slice, map, or - string of length zero. - Dot is unaffected. - - {{if pipeline}} T1 {{else}} T0 {{end}} - If the value of the pipeline is empty, T0 is executed; - otherwise, T1 is executed. Dot is unaffected. - - {{range pipeline}} T1 {{end}} - The value of the pipeline must be an array, slice, or map. If - the value of the pipeline has length zero, nothing is output; - otherwise, dot is set to the successive elements of the array, - slice, or map and T1 is executed. - - {{range pipeline}} T1 {{else}} T0 {{end}} - The value of the pipeline must be an array, slice, or map. If - the value of the pipeline has length zero, dot is unaffected and - T0 is executed; otherwise, dot is set to the successive elements - of the array, slice, or map and T1 is executed. - - {{template "name"}} - The template with the specified name is executed with nil data. - - {{template "name" pipeline}} - The template with the specified name is executed with dot set - to the value of the pipeline. - - {{with pipeline}} T1 {{end}} - If the value of the pipeline is empty, no output is generated; - otherwise, dot is set to the value of the pipeline and T1 is - executed. - - {{with pipeline}} T1 {{else}} T0 {{end}} - If the value of the pipeline is empty, dot is unaffected and T0 - is executed; otherwise, dot is set to the value of the pipeline - and T1 is executed. - -Arguments - -An argument is a simple value, denoted by one of the following. - - - A boolean, string, character, integer, floating-point, imaginary - or complex constant in Go syntax. These behave like Go's untyped - constants, although raw strings may not span newlines. - - The character '.' (period): - . - The result is the value of dot. - - A variable name, which is a (possibly empty) alphanumeric string - preceded by a dollar sign, such as - $piOver2 - or - $ - The result is the value of the variable. - Variables are described below. - - The name of a field of the data, which must be a struct, preceded - by a period, such as - .Field - The result is the value of the field. Field invocations may be - chained: - .Field1.Field2 - Fields can also be evaluated on variables, including chaining: - $x.Field1.Field2 - - The name of a niladic method of the data, preceded by a period, - such as - .Method - The result is the value of invoking the method with dot as the - receiver, dot.Method(). Such a method must have one return value (of - any type) or two return values, the second of which is an os.Error. - If it has two and the returned error is non-nil, execution terminates - and an error is returned to the caller as the value of Execute. - Method invocations may be chained and combined with fields - to any depth: - .Field1.Method1.Field2.Method2 - Methods can also be evaluated on variables, including chaining: - $x.Method1.Field - - The name of a niladic function, such as - fun - The result is the value of invoking the function, fun(). The return - types and values behave as in methods. Functions and function - names are described below. - -Arguments may evaluate to any type; if they are pointers the implementation -automatically indirects to the base type when required. - -A pipeline is a possibly chained sequence of "commands". A command is a simple -value (argument) or a function or method call, possibly with multiple arguments: - - Argument - The result is the value of evaluating the argument. - .Method [Argument...] - The method can be alone or the last element of a chain but, - unlike methods in the middle of a chain, it can take arguments. - The result is the value of calling the method with the - arguments: - dot.Method(Argument1, etc.) - functionName [Argument...] - The result is the value of calling the function associated - with the name: - function(Argument1, etc.) - Functions and function names are described below. - -Pipelines - -A pipeline may be "chained" by separating a sequence of commands with pipeline -characters '|'. In a chained pipeline, the result of the each command is -passed as the last argument of the following command. The output of the final -command in the pipeline is the value of the pipeline. - -The output of a command will be either one value or two values, the second of -which has type os.Error. If that second value is present and evaluates to -non-nil, execution terminates and the error is returned to the caller of -Execute. - -Variables - -A pipeline inside an action may initialize a variable to capture the result. -The initialization has syntax - - $variable := pipeline - -where $variable is the name of the variable. An action that declares a -variable produces no output. - -If a "range" action initializes a variable, the variable is set to the -successive elements of the iteration. Also, a "range" may declare two -variables, separated by a comma: - - $index, $element := pipeline - -in which case $index and $element are set to the successive values of the -array/slice index or map key and element, respectively. Note that if there is -only one variable, it is assigned the element; this is opposite to the -convention in Go range clauses. - -A variable's scope extends to the "end" action of the control structure ("if", -"with", or "range") in which it is declared, or to the end of the template if -there is no such control structure. A template invocation does not inherit -variables from the point of its invocation. - -When execution begins, $ is set to the data argument passed to Execute, that is, -to the starting value of dot. - -Examples - -Here are some example one-line templates demonstrating pipelines and variables. -All produce the quoted word "output": - - {{"\"output\""}} - A string constant. - {{`"output"`}} - A raw string constant. - {{printf "%q" "output"}} - A function call. - {{"output" | printf "%q"}} - A function call whose final argument comes from the previous - command. - {{"put" | printf "%s%s" "out" | printf "%q"}} - A more elaborate call. - {{"output" | printf "%s" | printf "%q"}} - A longer chain. - {{with "output"}}{{printf "%q" .}}{{end}} - A with action using dot. - {{with $x := "output" | printf "%q"}}{{$x}}{{end}} - A with action that creates and uses a variable. - {{with $x := "output"}}{{printf "%q" $x}}{{end}} - A with action that uses the variable in another action. - {{with $x := "output"}}{{$x | printf "%q"}}{{end}} - The same, but pipelined. - -Functions - -During execution functions are found in three function maps: first in the -template, then in the "template set" (described below), and finally in the -global function map. By default, no functions are defined in the template or -the set but the Funcs methods can be used to add them. - -Predefined global functions are named as follows. - - and - Returns the boolean AND of its arguments by returning the - first empty argument or the last argument, that is, - "and x y" behaves as "if x then y else x". All the - arguments are evaluated. - html - Returns the escaped HTML equivalent of the textual - representation of its arguments. - index - Returns the result of indexing its first argument by the - following arguments. Thus "index x 1 2 3" is, in Go syntax, - x[1][2][3]. Each indexed item must be a map, slice, or array. - js - Returns the escaped JavaScript equivalent of the textual - representation of its arguments. - not - Returns the boolean negation of its single argument. - or - Returns the boolean OR of its arguments by returning the - first non-empty argument or the last argument, that is, - "or x y" behaves as "if x then x else y". All the - arguments are evaluated. - print - An alias for fmt.Sprint - printf - An alias for fmt.Sprintf - println - An alias for fmt.Sprintln - url - Returns the escaped value of the textual representation of - its arguments in a form suitable for embedding in a URL. - -The boolean functions take any zero value to be false and a non-zero value to -be true. - -Template sets - -Each template is named by a string specified when it is created. A template may -use a template invocation to instantiate another template directly or by its -name; see the explanation of the template action above. The name is looked up -in the template set associated with the template. - -If no template invocation actions occur in the template, the issue of template -sets can be ignored. If it does contain invocations, though, the template -containing the invocations must be part of a template set in which to look up -the names. - -There are two ways to construct template sets. - -The first is to use a Set's Parse method to create a set of named templates from -a single input defining multiple templates. The syntax of the definitions is to -surround each template declaration with a define and end action. - -The define action names the template being created by providing a string -constant. Here is a simple example of input to Set.Parse: - - `{{define "T1"}} definition of template T1 {{end}} - {{define "T2"}} definition of template T2 {{end}} - {{define "T3"}} {{template "T1"}} {{template "T2"}} {{end}}` - -This defines two templates, T1 and T2, and a third T3 that invokes the other two -when it is executed. - -The second way to build a template set is to use Set's Add method to add a -parsed template to a set. A template may be bound at most one set. If it's -necessary to have a template in multiple sets, the template definition must be -parsed multiple times to create distinct *Template values. - -Set.Parse may be called multiple times on different inputs to construct the set. -Two sets may therefore be constructed with a common base set of templates plus, -through a second Parse call each, specializations for some elements. - -A template may be executed directly or through Set.Execute, which executes a -named template from the set. To invoke our example above, we might write, - - err := set.Execute(os.Stdout, "T3", "no data needed") - if err != nil { - log.Fatalf("execution failed: %s", err) - } -*/ -package template diff --git a/src/pkg/exp/template/exec.go b/src/pkg/exp/template/exec.go deleted file mode 100644 index 7d7a9c732..000000000 --- a/src/pkg/exp/template/exec.go +++ /dev/null @@ -1,633 +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 template - -import ( - "exp/template/parse" - "fmt" - "io" - "os" - "reflect" - "runtime" - "strings" -) - -// state represents the state of an execution. It's not part of the -// template so that multiple executions of the same template -// can execute in parallel. -type state struct { - tmpl *Template - wr io.Writer - line int // line number for errors - vars []variable // push-down stack of variable values. -} - -// variable holds the dynamic value of a variable such as $, $x etc. -type variable struct { - name string - value reflect.Value -} - -// push pushes a new variable on the stack. -func (s *state) push(name string, value reflect.Value) { - s.vars = append(s.vars, variable{name, value}) -} - -// mark returns the length of the variable stack. -func (s *state) mark() int { - return len(s.vars) -} - -// pop pops the variable stack up to the mark. -func (s *state) pop(mark int) { - s.vars = s.vars[0:mark] -} - -// setVar overwrites the top-nth variable on the stack. Used by range iterations. -func (s *state) setVar(n int, value reflect.Value) { - s.vars[len(s.vars)-n].value = value -} - -// varValue returns the value of the named variable. -func (s *state) varValue(name string) reflect.Value { - for i := s.mark() - 1; i >= 0; i-- { - if s.vars[i].name == name { - return s.vars[i].value - } - } - s.errorf("undefined variable: %s", name) - return zero -} - -var zero reflect.Value - -// errorf formats the error and terminates processing. -func (s *state) errorf(format string, args ...interface{}) { - format = fmt.Sprintf("template: %s:%d: %s", s.tmpl.Name(), s.line, format) - panic(fmt.Errorf(format, args...)) -} - -// error terminates processing. -func (s *state) error(err os.Error) { - s.errorf("%s", err) -} - -// errRecover is the handler that turns panics into returns from the top -// level of Parse. -func errRecover(errp *os.Error) { - e := recover() - if e != nil { - if _, ok := e.(runtime.Error); ok { - panic(e) - } - *errp = e.(os.Error) - } -} - -// Execute applies a parsed template to the specified data object, -// writing the output to wr. -func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) { - defer errRecover(&err) - value := reflect.ValueOf(data) - state := &state{ - tmpl: t, - wr: wr, - line: 1, - vars: []variable{{"$", value}}, - } - if t.Root == nil { - state.errorf("must be parsed before execution") - } - state.walk(value, t.Root) - return -} - -// Walk functions step through the major pieces of the template structure, -// generating output as they go. -func (s *state) walk(dot reflect.Value, n parse.Node) { - switch n := n.(type) { - case *parse.ActionNode: - s.line = n.Line - // Do not pop variables so they persist until next end. - // Also, if the action declares variables, don't print the result. - val := s.evalPipeline(dot, n.Pipe) - if len(n.Pipe.Decl) == 0 { - s.printValue(n, val) - } - case *parse.IfNode: - s.line = n.Line - s.walkIfOrWith(parse.NodeIf, dot, n.Pipe, n.List, n.ElseList) - case *parse.ListNode: - for _, node := range n.Nodes { - s.walk(dot, node) - } - case *parse.RangeNode: - s.line = n.Line - s.walkRange(dot, n) - case *parse.TemplateNode: - s.line = n.Line - s.walkTemplate(dot, n) - case *parse.TextNode: - if _, err := s.wr.Write(n.Text); err != nil { - s.error(err) - } - case *parse.WithNode: - s.line = n.Line - s.walkIfOrWith(parse.NodeWith, dot, n.Pipe, n.List, n.ElseList) - default: - s.errorf("unknown node: %s", n) - } -} - -// walkIfOrWith walks an 'if' or 'with' node. The two control structures -// are identical in behavior except that 'with' sets dot. -func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) { - defer s.pop(s.mark()) - val := s.evalPipeline(dot, pipe) - truth, ok := isTrue(val) - if !ok { - s.errorf("if/with can't use value of type %T", val.Interface()) - } - if truth { - if typ == parse.NodeWith { - s.walk(val, list) - } else { - s.walk(dot, list) - } - } else if elseList != nil { - s.walk(dot, elseList) - } -} - -// isTrue returns whether the value is 'true', in the sense of not the zero of its type, -// and whether the value has a meaningful truth value. -func isTrue(val reflect.Value) (truth, ok bool) { - switch val.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - truth = val.Len() > 0 - case reflect.Bool: - truth = val.Bool() - case reflect.Complex64, reflect.Complex128: - truth = val.Complex() != 0 - case reflect.Chan, reflect.Func, reflect.Ptr: - truth = !val.IsNil() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - truth = val.Int() != 0 - case reflect.Float32, reflect.Float64: - truth = val.Float() != 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - truth = val.Uint() != 0 - case reflect.Struct: - truth = true // Struct values are always true. - default: - return - } - return truth, true -} - -func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { - defer s.pop(s.mark()) - val, _ := indirect(s.evalPipeline(dot, r.Pipe)) - // mark top of stack before any variables in the body are pushed. - mark := s.mark() - switch val.Kind() { - case reflect.Array, reflect.Slice: - if val.Len() == 0 { - break - } - for i := 0; i < val.Len(); i++ { - elem := val.Index(i) - // Set top var (lexically the second if there are two) to the element. - if len(r.Pipe.Decl) > 0 { - s.setVar(1, elem) - } - // Set next var (lexically the first if there are two) to the index. - if len(r.Pipe.Decl) > 1 { - s.setVar(2, reflect.ValueOf(i)) - } - s.walk(elem, r.List) - s.pop(mark) - } - return - case reflect.Map: - if val.Len() == 0 { - break - } - for _, key := range val.MapKeys() { - elem := val.MapIndex(key) - // Set top var (lexically the second if there are two) to the element. - if len(r.Pipe.Decl) > 0 { - s.setVar(1, elem) - } - // Set next var (lexically the first if there are two) to the key. - if len(r.Pipe.Decl) > 1 { - s.setVar(2, key) - } - s.walk(elem, r.List) - s.pop(mark) - } - return - default: - s.errorf("range can't iterate over value of type %T", val.Interface()) - } - if r.ElseList != nil { - s.walk(dot, r.ElseList) - } -} - -func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) { - set := s.tmpl.set - if set == nil { - s.errorf("no set defined in which to invoke template named %q", t.Name) - } - tmpl := set.tmpl[t.Name] - if tmpl == nil { - s.errorf("template %q not in set", t.Name) - } - // Variables declared by the pipeline persist. - dot = s.evalPipeline(dot, t.Pipe) - newState := *s - newState.tmpl = tmpl - // No dynamic scoping: template invocations inherit no variables. - newState.vars = []variable{{"$", dot}} - newState.walk(dot, tmpl.Root) -} - -// Eval functions evaluate pipelines, commands, and their elements and extract -// values from the data structure by examining fields, calling methods, and so on. -// The printing of those values happens only through walk functions. - -// evalPipeline returns the value acquired by evaluating a pipeline. If the -// pipeline has a variable declaration, the variable will be pushed on the -// stack. Callers should therefore pop the stack after they are finished -// executing commands depending on the pipeline value. -func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value reflect.Value) { - if pipe == nil { - return - } - for _, cmd := range pipe.Cmds { - value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg. - // If the object has type interface{}, dig down one level to the thing inside. - if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 { - value = reflect.ValueOf(value.Interface()) // lovely! - } - } - for _, variable := range pipe.Decl { - s.push(variable.Ident[0], value) - } - return value -} - -func (s *state) notAFunction(args []parse.Node, final reflect.Value) { - if len(args) > 1 || final.IsValid() { - s.errorf("can't give argument to non-function %s", args[0]) - } -} - -func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value { - firstWord := cmd.Args[0] - switch n := firstWord.(type) { - case *parse.FieldNode: - return s.evalFieldNode(dot, n, cmd.Args, final) - case *parse.IdentifierNode: - // Must be a function. - return s.evalFunction(dot, n.Ident, cmd.Args, final) - case *parse.VariableNode: - return s.evalVariableNode(dot, n, cmd.Args, final) - } - s.notAFunction(cmd.Args, final) - switch word := firstWord.(type) { - case *parse.BoolNode: - return reflect.ValueOf(word.True) - case *parse.DotNode: - return dot - case *parse.NumberNode: - return s.idealConstant(word) - case *parse.StringNode: - return reflect.ValueOf(word.Text) - } - s.errorf("can't evaluate command %q", firstWord) - panic("not reached") -} - -// idealConstant is called to return the value of a number in a context where -// we don't know the type. In that case, the syntax of the number tells us -// its type, and we use Go rules to resolve. Note there is no such thing as -// a uint ideal constant in this situation - the value must be of int type. -func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value { - // These are ideal constants but we don't know the type - // and we have no context. (If it was a method argument, - // we'd know what we need.) The syntax guides us to some extent. - switch { - case constant.IsComplex: - return reflect.ValueOf(constant.Complex128) // incontrovertible. - case constant.IsFloat && strings.IndexAny(constant.Text, ".eE") >= 0: - return reflect.ValueOf(constant.Float64) - case constant.IsInt: - n := int(constant.Int64) - if int64(n) != constant.Int64 { - s.errorf("%s overflows int", constant.Text) - } - return reflect.ValueOf(n) - case constant.IsUint: - s.errorf("%s overflows int", constant.Text) - } - return zero -} - -func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value { - return s.evalFieldChain(dot, dot, field.Ident, args, final) -} - -func (s *state) evalVariableNode(dot reflect.Value, v *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value { - // $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields. - value := s.varValue(v.Ident[0]) - if len(v.Ident) == 1 { - return value - } - return s.evalFieldChain(dot, value, v.Ident[1:], args, final) -} - -// evalFieldChain evaluates .X.Y.Z possibly followed by arguments. -// dot is the environment in which to evaluate arguments, while -// receiver is the value being walked along the chain. -func (s *state) evalFieldChain(dot, receiver reflect.Value, ident []string, args []parse.Node, final reflect.Value) reflect.Value { - n := len(ident) - for i := 0; i < n-1; i++ { - receiver = s.evalField(dot, ident[i], nil, zero, receiver) - } - // Now if it's a method, it gets the arguments. - return s.evalField(dot, ident[n-1], args, final, receiver) -} - -func (s *state) evalFunction(dot reflect.Value, name string, args []parse.Node, final reflect.Value) reflect.Value { - function, ok := findFunction(name, s.tmpl, s.tmpl.set) - if !ok { - s.errorf("%q is not a defined function", name) - } - return s.evalCall(dot, function, name, args, final) -} - -// evalField evaluates an expression like (.Field) or (.Field arg1 arg2). -// The 'final' argument represents the return value from the preceding -// value of the pipeline, if any. -func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node, final, receiver reflect.Value) reflect.Value { - if !receiver.IsValid() { - return zero - } - typ := receiver.Type() - receiver, _ = indirect(receiver) - // Unless it's an interface, need to get to a value of type *T to guarantee - // we see all methods of T and *T. - ptr := receiver - if ptr.Kind() != reflect.Interface && ptr.CanAddr() { - ptr = ptr.Addr() - } - if method, ok := methodByName(ptr, fieldName); ok { - return s.evalCall(dot, method, fieldName, args, final) - } - // It's not a method; is it a field of a struct? - receiver, isNil := indirect(receiver) - if receiver.Kind() == reflect.Struct { - tField, ok := receiver.Type().FieldByName(fieldName) - if ok { - field := receiver.FieldByIndex(tField.Index) - if len(args) > 1 || final.IsValid() { - s.errorf("%s is not a method but has arguments", fieldName) - } - if tField.PkgPath == "" { // field is exported - return field - } - } - } - if isNil { - s.errorf("nil pointer evaluating %s.%s", typ, fieldName) - } - s.errorf("can't evaluate field %s in type %s", fieldName, typ) - panic("not reached") -} - -// TODO: delete when reflect's own MethodByName is released. -func methodByName(receiver reflect.Value, name string) (reflect.Value, bool) { - typ := receiver.Type() - for i := 0; i < typ.NumMethod(); i++ { - if typ.Method(i).Name == name { - return receiver.Method(i), true // This value includes the receiver. - } - } - return zero, false -} - -var ( - osErrorType = reflect.TypeOf(new(os.Error)).Elem() -) - -// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so -// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0] -// as the function itself. -func (s *state) evalCall(dot, fun reflect.Value, name string, args []parse.Node, final reflect.Value) reflect.Value { - if args != nil { - args = args[1:] // Zeroth arg is function name/node; not passed to function. - } - typ := fun.Type() - numIn := len(args) - if final.IsValid() { - numIn++ - } - numFixed := len(args) - if typ.IsVariadic() { - numFixed = typ.NumIn() - 1 // last arg is the variadic one. - if numIn < numFixed { - s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) - } - } else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() { - s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args)) - } - if !goodFunc(typ) { - s.errorf("can't handle multiple results from method/function %q", name) - } - // Build the arg list. - argv := make([]reflect.Value, numIn) - // Args must be evaluated. Fixed args first. - i := 0 - for ; i < numFixed; i++ { - argv[i] = s.evalArg(dot, typ.In(i), args[i]) - } - // Now the ... args. - if typ.IsVariadic() { - argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice. - for ; i < len(args); i++ { - argv[i] = s.evalArg(dot, argType, args[i]) - } - } - // Add final value if necessary. - if final.IsValid() { - argv[i] = final - } - result := fun.Call(argv) - // If we have an os.Error that is not nil, stop execution and return that error to the caller. - if len(result) == 2 && !result[1].IsNil() { - s.errorf("error calling %s: %s", name, result[1].Interface().(os.Error)) - } - return result[0] -} - -// validateType guarantees that the value is valid and assignable to the type. -func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value { - if !value.IsValid() { - s.errorf("invalid value; expected %s", typ) - } - if !value.Type().AssignableTo(typ) { - s.errorf("wrong type for value; expected %s; got %s", typ, value.Type()) - } - return value -} - -func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value { - switch arg := n.(type) { - case *parse.DotNode: - return s.validateType(dot, typ) - case *parse.FieldNode: - return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, zero), typ) - case *parse.VariableNode: - return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ) - } - switch typ.Kind() { - case reflect.Bool: - return s.evalBool(typ, n) - case reflect.Complex64, reflect.Complex128: - return s.evalComplex(typ, n) - case reflect.Float32, reflect.Float64: - return s.evalFloat(typ, n) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return s.evalInteger(typ, n) - case reflect.Interface: - if typ.NumMethod() == 0 { - return s.evalEmptyInterface(dot, n) - } - case reflect.String: - return s.evalString(typ, n) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return s.evalUnsignedInteger(typ, n) - } - s.errorf("can't handle %s for arg of type %s", n, typ) - panic("not reached") -} - -func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value { - if n, ok := n.(*parse.BoolNode); ok { - value := reflect.New(typ).Elem() - value.SetBool(n.True) - return value - } - s.errorf("expected bool; found %s", n) - panic("not reached") -} - -func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value { - if n, ok := n.(*parse.StringNode); ok { - value := reflect.New(typ).Elem() - value.SetString(n.Text) - return value - } - s.errorf("expected string; found %s", n) - panic("not reached") -} - -func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value { - if n, ok := n.(*parse.NumberNode); ok && n.IsInt { - value := reflect.New(typ).Elem() - value.SetInt(n.Int64) - return value - } - s.errorf("expected integer; found %s", n) - panic("not reached") -} - -func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value { - if n, ok := n.(*parse.NumberNode); ok && n.IsUint { - value := reflect.New(typ).Elem() - value.SetUint(n.Uint64) - return value - } - s.errorf("expected unsigned integer; found %s", n) - panic("not reached") -} - -func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value { - if n, ok := n.(*parse.NumberNode); ok && n.IsFloat { - value := reflect.New(typ).Elem() - value.SetFloat(n.Float64) - return value - } - s.errorf("expected float; found %s", n) - panic("not reached") -} - -func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value { - if n, ok := n.(*parse.NumberNode); ok && n.IsComplex { - value := reflect.New(typ).Elem() - value.SetComplex(n.Complex128) - return value - } - s.errorf("expected complex; found %s", n) - panic("not reached") -} - -func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value { - switch n := n.(type) { - case *parse.BoolNode: - return reflect.ValueOf(n.True) - case *parse.DotNode: - return dot - case *parse.FieldNode: - return s.evalFieldNode(dot, n, nil, zero) - case *parse.IdentifierNode: - return s.evalFunction(dot, n.Ident, nil, zero) - case *parse.NumberNode: - return s.idealConstant(n) - case *parse.StringNode: - return reflect.ValueOf(n.Text) - case *parse.VariableNode: - return s.evalVariableNode(dot, n, nil, zero) - } - s.errorf("can't handle assignment of %s to empty interface argument", n) - panic("not reached") -} - -// indirect returns the item at the end of indirection, and a bool to indicate if it's nil. -// We indirect through pointers and empty interfaces (only) because -// non-empty interfaces have methods we might need. -func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { - for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { - if v.IsNil() { - return v, true - } - if v.Kind() == reflect.Interface && v.NumMethod() > 0 { - break - } - } - return v, false -} - -// printValue writes the textual representation of the value to the output of -// the template. -func (s *state) printValue(n parse.Node, v reflect.Value) { - if !v.IsValid() { - fmt.Fprint(s.wr, "") - return - } - switch v.Kind() { - case reflect.Ptr: - var isNil bool - if v, isNil = indirect(v); isNil { - fmt.Fprint(s.wr, "") - return - } - case reflect.Chan, reflect.Func, reflect.Interface: - s.errorf("can't print %s of type %s", n, v.Type()) - } - fmt.Fprint(s.wr, v.Interface()) -} diff --git a/src/pkg/exp/template/exec_test.go b/src/pkg/exp/template/exec_test.go deleted file mode 100644 index b78847440..000000000 --- a/src/pkg/exp/template/exec_test.go +++ /dev/null @@ -1,533 +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 template - -import ( - "bytes" - "flag" - "fmt" - "os" - "reflect" - "sort" - "strings" - "testing" -) - -var debug = flag.Bool("debug", false, "show the errors produced by the tests") - -// T has lots of interesting pieces to use to test execution. -type T struct { - // Basics - True bool - I int - U16 uint16 - X string - FloatZero float64 - ComplexZero float64 - // Nested structs. - U *U - // Slices - SI []int - SIEmpty []int - SB []bool - // Maps - MSI map[string]int - MSIone map[string]int // one element, for deterministic output - MSIEmpty map[string]int - SMSI []map[string]int - // Empty interfaces; used to see if we can dig inside one. - Empty0 interface{} // nil - Empty1 interface{} - Empty2 interface{} - Empty3 interface{} - Empty4 interface{} - // Non-empty interface. - NonEmptyInterface I - // Pointers - PI *int - PSI *[]int - NIL *int - // Template to test evaluation of templates. - Tmpl *Template -} - -type U struct { - V string -} - -var tVal = &T{ - True: true, - I: 17, - U16: 16, - X: "x", - U: &U{"v"}, - SI: []int{3, 4, 5}, - SB: []bool{true, false}, - MSI: map[string]int{"one": 1, "two": 2, "three": 3}, - MSIone: map[string]int{"one": 1}, - SMSI: []map[string]int{ - {"one": 1, "two": 2}, - {"eleven": 11, "twelve": 12}, - }, - Empty1: 3, - Empty2: "empty2", - Empty3: []int{7, 8}, - Empty4: &U{"UinEmpty"}, - NonEmptyInterface: new(T), - PI: newInt(23), - PSI: newIntSlice(21, 22, 23), - Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X -} - -// A non-empty interface. -type I interface { - Method0() string -} - -var iVal I = tVal - -// Helpers for creation. -func newInt(n int) *int { - p := new(int) - *p = n - return p -} - -func newIntSlice(n ...int) *[]int { - p := new([]int) - *p = make([]int, len(n)) - copy(*p, n) - return p -} - -// Simple methods with and without arguments. -func (t *T) Method0() string { - return "M0" -} - -func (t *T) Method1(a int) int { - return a -} - -func (t *T) Method2(a uint16, b string) string { - return fmt.Sprintf("Method2: %d %s", a, b) -} - -func (t *T) MAdd(a int, b []int) []int { - v := make([]int, len(b)) - for i, x := range b { - v[i] = x + a - } - return v -} - -// MSort is used to sort map keys for stable output. (Nice trick!) -func (t *T) MSort(m map[string]int) []string { - keys := make([]string, len(m)) - i := 0 - for k := range m { - keys[i] = k - i++ - } - sort.Strings(keys) - return keys -} - -// EPERM returns a value and an os.Error according to its argument. -func (t *T) EPERM(error bool) (bool, os.Error) { - if error { - return true, os.EPERM - } - return false, nil -} - -// A few methods to test chaining. -func (t *T) GetU() *U { - return t.U -} - -func (u *U) TrueFalse(b bool) string { - if b { - return "true" - } - return "" -} - -func typeOf(arg interface{}) string { - return fmt.Sprintf("%T", arg) -} - -type execTest struct { - name string - input string - output string - data interface{} - ok bool -} - -// bigInt and bigUint are hex string representing numbers either side -// of the max int boundary. -// We do it this way so the test doesn't depend on ints being 32 bits. -var ( - bigInt = fmt.Sprintf("0x%x", int(1<", "<13>", 13, true}, - {"dot uint", "<{{.}}>", "<14>", uint(14), true}, - {"dot float", "<{{.}}>", "<15.1>", 15.1, true}, - {"dot bool", "<{{.}}>", "", true, true}, - {"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true}, - {"dot string", "<{{.}}>", "", "hello", true}, - {"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true}, - {"dot map", "<{{.}}>", "", map[string]int{"one": 11, "two": 22}, true}, - {"dot struct", "<{{.}}>", "<{7 seven}>", struct { - a int - b string - }{7, "seven"}, true}, - - // Variables. - {"$ int", "{{$}}", "123", 123, true}, - {"$.I", "{{$.I}}", "17", tVal, true}, - {"$.U.V", "{{$.U.V}}", "v", tVal, true}, - {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true}, - - // Pointers. - {"*int", "{{.PI}}", "23", tVal, true}, - {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true}, - {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true}, - {"NIL", "{{.NIL}}", "", tVal, true}, - - // Empty interfaces holding values. - {"empty nil", "{{.Empty0}}", "", tVal, true}, - {"empty with int", "{{.Empty1}}", "3", tVal, true}, - {"empty with string", "{{.Empty2}}", "empty2", tVal, true}, - {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true}, - {"empty with struct", "{{.Empty4}}", "{UinEmpty}", tVal, true}, - {"empty with struct, field", "{{.Empty4.V}}", "UinEmpty", tVal, true}, - - // Method calls. - {".Method0", "-{{.Method0}}-", "-M0-", tVal, true}, - {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true}, - {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true}, - {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true}, - {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true}, - {".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true}, - {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true}, - {"method on chained var", - "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", - "true", tVal, true}, - {"chained method", - "{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", - "true", tVal, true}, - {"chained method on variable", - "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}", - "true", tVal, true}, - - // Pipelines. - {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true}, - - // If. - {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true}, - {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true}, - {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, - {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, - {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, - {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, - {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, - {"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, - {"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true}, - {"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true}, - - // Print etc. - {"print", `{{print "hello, print"}}`, "hello, print", tVal, true}, - {"print", `{{print 1 2 3}}`, "1 2 3", tVal, true}, - {"println", `{{println 1 2 3}}`, "1 2 3\n", tVal, true}, - {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true}, - {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true}, - {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true}, - {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true}, - {"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true}, - {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true}, - {"printf method", `{{printf "%s" .Method0}}`, "M0", tVal, true}, - {"printf dot", `{{with .I}}{{printf "%d" .}}{{end}}`, "17", tVal, true}, - {"printf var", `{{with $x := .I}}{{printf "%d" $x}}{{end}}`, "17", tVal, true}, - {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true}, - - // HTML. - {"html", `{{html ""}}`, - "<script>alert("XSS");</script>", nil, true}, - {"html pipeline", `{{printf "" | html}}`, - "<script>alert("XSS");</script>", nil, true}, - - // JavaScript. - {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true}, - - // URL. - {"url", `{{"http://www.example.org/"|url}}`, "http%3A%2F%2Fwww.example.org%2F", nil, true}, - - // Booleans - {"not", "{{not true}} {{not false}}", "false true", nil, true}, - {"and", "{{and false 0}} {{and 1 0}} {{and 0 true}} {{and 1 1}}", "false 0 0 1", nil, true}, - {"or", "{{or 0 0}} {{or 1 0}} {{or 0 true}} {{or 1 1}}", "0 1 true 1", nil, true}, - {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true}, - {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true}, - - // Indexing. - {"slice[0]", "{{index .SI 0}}", "3", tVal, true}, - {"slice[1]", "{{index .SI 1}}", "4", tVal, true}, - {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false}, - {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false}, - {"map[one]", "{{index .MSI `one`}}", "1", tVal, true}, - {"map[two]", "{{index .MSI `two`}}", "2", tVal, true}, - {"map[NO]", "{{index .MSI `XXX`}}", "", tVal, true}, - {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false}, - {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true}, - - // With. - {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true}, - {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true}, - {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true}, - {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true}, - {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0+1.5i)", tVal, true}, - {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true}, - {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true}, - {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true}, - {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "UinEmpty", tVal, true}, - {"with $x int", "{{with $x := .I}}{{$x}}{{end}}", "17", tVal, true}, - {"with $x struct.U.V", "{{with $x := $}}{{$x.U.V}}{{end}}", "v", tVal, true}, - {"with variable and action", "{{with $x := $}}{{$y := $.U.V}}{{$y}}{{end}}", "v", tVal, true}, - - // Range. - {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true}, - {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, - {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, - {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, - {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, - {"range map", "{{range .MSI | .MSort}}-{{.}}-{{end}}", "-one--three--two-", tVal, true}, - {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true}, - {"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", tVal, true}, - {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true}, - {"range $x SI", "{{range $x := .SI}}<{{$x}}>{{end}}", "<3><4><5>", tVal, true}, - {"range $x $y SI", "{{range $x, $y := .SI}}<{{$x}}={{$y}}>{{end}}", "<0=3><1=4><2=5>", tVal, true}, - {"range $x MSIone", "{{range $x := .MSIone}}<{{$x}}>{{end}}", "<1>", tVal, true}, - {"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "", tVal, true}, - {"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true}, - {"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true}, - - // Cute examples. - {"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true}, - {"or as if false", `{{or .SIEmpty "slice is empty"}}`, "slice is empty", tVal, true}, - - // Error handling. - {"error method, error", "{{.EPERM true}}", "", tVal, false}, - {"error method, no error", "{{.EPERM false}}", "false", tVal, true}, - - // Fixed bugs. - // Must separate dot and receiver; otherwise args are evaluated with dot set to variable. - {"bug0", "{{range .MSIone}}{{if $.Method1 .}}X{{end}}{{end}}", "X", tVal, true}, - // Do not loop endlessly in indirect for non-empty interfaces. - // The bug appears with *interface only; looped forever. - {"bug1", "{{.Method0}}", "M0", &iVal, true}, - // Was taking address of interface field, so method set was empty. - {"bug2", "{{$.NonEmptyInterface.Method0}}", "M0", tVal, true}, - // Struct values were not legal in with - mere oversight. - {"bug4", "{{with $}}{{.Method0}}{{end}}", "M0", tVal, true}, -} - -func zeroArgs() string { - return "zeroArgs" -} - -func oneArg(a string) string { - return "oneArg=" + a -} - -func testExecute(execTests []execTest, set *Set, t *testing.T) { - b := new(bytes.Buffer) - funcs := FuncMap{"zeroArgs": zeroArgs, "oneArg": oneArg, "typeOf": typeOf} - for _, test := range execTests { - tmpl := New(test.name).Funcs(funcs) - _, err := tmpl.ParseInSet(test.input, set) - if err != nil { - t.Errorf("%s: parse error: %s", test.name, err) - continue - } - b.Reset() - err = tmpl.Execute(b, test.data) - switch { - case !test.ok && err == nil: - t.Errorf("%s: expected error; got none", test.name) - continue - case test.ok && err != nil: - t.Errorf("%s: unexpected execute error: %s", test.name, err) - continue - case !test.ok && err != nil: - // expected error, got one - if *debug { - fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) - } - } - result := b.String() - if result != test.output { - t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result) - } - } -} - -func TestExecute(t *testing.T) { - testExecute(execTests, nil, t) -} - -// Check that an error from a method flows back to the top. -func TestExecuteError(t *testing.T) { - b := new(bytes.Buffer) - tmpl := New("error") - _, err := tmpl.Parse("{{.EPERM true}}") - if err != nil { - t.Fatalf("parse error: %s", err) - } - err = tmpl.Execute(b, tVal) - if err == nil { - t.Errorf("expected error; got none") - } else if !strings.Contains(err.String(), os.EPERM.String()) { - if *debug { - fmt.Printf("test execute error: %s\n", err) - } - t.Errorf("expected os.EPERM; got %s", err) - } -} - -func TestJSEscaping(t *testing.T) { - testCases := []struct { - in, exp string - }{ - {`a`, `a`}, - {`'foo`, `\'foo`}, - {`Go "jump" \`, `Go \"jump\" \\`}, - {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, - {"unprintable \uFDFF", `unprintable \uFDFF`}, - {``, `\x3Chtml\x3E`}, - } - for _, tc := range testCases { - s := JSEscapeString(tc.in) - if s != tc.exp { - t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp) - } - } -} - -// A nice example: walk a binary tree. - -type Tree struct { - Val int - Left, Right *Tree -} - -const treeTemplate = ` - {{define "tree"}} - [ - {{.Val}} - {{with .Left}} - {{template "tree" .}} - {{end}} - {{with .Right}} - {{template "tree" .}} - {{end}} - ] - {{end}} -` - -func TestTree(t *testing.T) { - var tree = &Tree{ - 1, - &Tree{ - 2, &Tree{ - 3, - &Tree{ - 4, nil, nil, - }, - nil, - }, - &Tree{ - 5, - &Tree{ - 6, nil, nil, - }, - nil, - }, - }, - &Tree{ - 7, - &Tree{ - 8, - &Tree{ - 9, nil, nil, - }, - nil, - }, - &Tree{ - 10, - &Tree{ - 11, nil, nil, - }, - nil, - }, - }, - } - set := new(Set) - _, err := set.Parse(treeTemplate) - if err != nil { - t.Fatal("parse error:", err) - } - var b bytes.Buffer - err = set.Execute(&b, "tree", tree) - if err != nil { - t.Fatal("exec error:", err) - } - stripSpace := func(r int) int { - if r == '\t' || r == '\n' { - return -1 - } - return r - } - result := strings.Map(stripSpace, b.String()) - const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]" - if result != expect { - t.Errorf("expected %q got %q", expect, result) - } -} diff --git a/src/pkg/exp/template/funcs.go b/src/pkg/exp/template/funcs.go deleted file mode 100644 index 579c70099..000000000 --- a/src/pkg/exp/template/funcs.go +++ /dev/null @@ -1,352 +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 template - -import ( - "bytes" - "fmt" - "http" - "io" - "os" - "reflect" - "strings" - "unicode" - "utf8" -) - -// FuncMap is the type of the map defining the mapping from names to functions. -// Each function must have either a single return value, or two return values of -// which the second has type os.Error. If the second argument evaluates to non-nil -// during execution, execution terminates and Execute returns an error. -type FuncMap map[string]interface{} - -var builtins = FuncMap{ - "and": and, - "html": HTMLEscaper, - "index": index, - "js": JSEscaper, - "not": not, - "or": or, - "print": fmt.Sprint, - "printf": fmt.Sprintf, - "println": fmt.Sprintln, - "url": URLEscaper, -} - -var builtinFuncs = createValueFuncs(builtins) - -// createValueFuncs turns a FuncMap into a map[string]reflect.Value -func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { - m := make(map[string]reflect.Value) - addValueFuncs(m, funcMap) - return m -} - -// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values. -func addValueFuncs(out map[string]reflect.Value, in FuncMap) { - for name, fn := range in { - v := reflect.ValueOf(fn) - if v.Kind() != reflect.Func { - panic("value for " + name + " not a function") - } - if !goodFunc(v.Type()) { - panic(fmt.Errorf("can't handle multiple results from method/function %q", name)) - } - out[name] = v - } -} - -// addFuncs adds to values the functions in funcs. It does no checking of the input - -// call addValueFuncs first. -func addFuncs(out, in FuncMap) { - for name, fn := range in { - out[name] = fn - } -} - -// goodFunc checks that the function or method has the right result signature. -func goodFunc(typ reflect.Type) bool { - // We allow functions with 1 result or 2 results where the second is an os.Error. - switch { - case typ.NumOut() == 1: - return true - case typ.NumOut() == 2 && typ.Out(1) == osErrorType: - return true - } - return false -} - -// findFunction looks for a function in the template, set, and global map. -func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) { - if tmpl != nil { - if fn := tmpl.execFuncs[name]; fn.IsValid() { - return fn, true - } - } - if set != nil { - if fn := set.execFuncs[name]; fn.IsValid() { - return fn, true - } - } - if fn := builtinFuncs[name]; fn.IsValid() { - return fn, true - } - return reflect.Value{}, false -} - -// Indexing. - -// index returns the result of indexing its first argument by the following -// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each -// indexed item must be a map, slice, or array. -func index(item interface{}, indices ...interface{}) (interface{}, os.Error) { - v := reflect.ValueOf(item) - for _, i := range indices { - index := reflect.ValueOf(i) - var isNil bool - if v, isNil = indirect(v); isNil { - return nil, fmt.Errorf("index of nil pointer") - } - switch v.Kind() { - case reflect.Array, reflect.Slice: - var x int64 - switch index.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - x = index.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - x = int64(index.Uint()) - default: - return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type()) - } - if x < 0 || x >= int64(v.Len()) { - return nil, fmt.Errorf("index out of range: %d", x) - } - v = v.Index(int(x)) - case reflect.Map: - if !index.Type().AssignableTo(v.Type().Key()) { - return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type()) - } - if x := v.MapIndex(index); x.IsValid() { - v = x - } else { - v = reflect.Zero(v.Type().Key()) - } - default: - return nil, fmt.Errorf("can't index item of type %s", index.Type()) - } - } - return v.Interface(), nil -} - -// Boolean logic. - -func truth(a interface{}) bool { - t, _ := isTrue(reflect.ValueOf(a)) - return t -} - -// and computes the Boolean AND of its arguments, returning -// the first false argument it encounters, or the last argument. -func and(arg0 interface{}, args ...interface{}) interface{} { - if !truth(arg0) { - return arg0 - } - for i := range args { - arg0 = args[i] - if !truth(arg0) { - break - } - } - return arg0 -} - -// or computes the Boolean OR of its arguments, returning -// the first true argument it encounters, or the last argument. -func or(arg0 interface{}, args ...interface{}) interface{} { - if truth(arg0) { - return arg0 - } - for i := range args { - arg0 = args[i] - if truth(arg0) { - break - } - } - return arg0 -} - -// not returns the Boolean negation of its argument. -func not(arg interface{}) (truth bool) { - truth, _ = isTrue(reflect.ValueOf(arg)) - return !truth -} - -// HTML escaping. - -var ( - htmlQuot = []byte(""") // shorter than """ - htmlApos = []byte("'") // shorter than "'" - htmlAmp = []byte("&") - htmlLt = []byte("<") - htmlGt = []byte(">") -) - -// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. -func HTMLEscape(w io.Writer, b []byte) { - last := 0 - for i, c := range b { - var html []byte - switch c { - case '"': - html = htmlQuot - case '\'': - html = htmlApos - case '&': - html = htmlAmp - case '<': - html = htmlLt - case '>': - html = htmlGt - default: - continue - } - w.Write(b[last:i]) - w.Write(html) - last = i + 1 - } - w.Write(b[last:]) -} - -// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. -func HTMLEscapeString(s string) string { - // Avoid allocation if we can. - if strings.IndexAny(s, `'"&<>`) < 0 { - return s - } - var b bytes.Buffer - HTMLEscape(&b, []byte(s)) - return b.String() -} - -// HTMLEscaper returns the escaped HTML equivalent of the textual -// representation of its arguments. -func HTMLEscaper(args ...interface{}) string { - ok := false - var s string - if len(args) == 1 { - s, ok = args[0].(string) - } - if !ok { - s = fmt.Sprint(args...) - } - return HTMLEscapeString(s) -} - -// JavaScript escaping. - -var ( - jsLowUni = []byte(`\u00`) - hex = []byte("0123456789ABCDEF") - - jsBackslash = []byte(`\\`) - jsApos = []byte(`\'`) - jsQuot = []byte(`\"`) - jsLt = []byte(`\x3C`) - jsGt = []byte(`\x3E`) -) - -// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. -func JSEscape(w io.Writer, b []byte) { - last := 0 - for i := 0; i < len(b); i++ { - c := b[i] - - if !jsIsSpecial(int(c)) { - // fast path: nothing to do - continue - } - w.Write(b[last:i]) - - if c < utf8.RuneSelf { - // Quotes, slashes and angle brackets get quoted. - // Control characters get written as \u00XX. - switch c { - case '\\': - w.Write(jsBackslash) - case '\'': - w.Write(jsApos) - case '"': - w.Write(jsQuot) - case '<': - w.Write(jsLt) - case '>': - w.Write(jsGt) - default: - w.Write(jsLowUni) - t, b := c>>4, c&0x0f - w.Write(hex[t : t+1]) - w.Write(hex[b : b+1]) - } - } else { - // Unicode rune. - rune, size := utf8.DecodeRune(b[i:]) - if unicode.IsPrint(rune) { - w.Write(b[i : i+size]) - } else { - // TODO(dsymonds): Do this without fmt? - fmt.Fprintf(w, "\\u%04X", rune) - } - i += size - 1 - } - last = i + 1 - } - w.Write(b[last:]) -} - -// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s. -func JSEscapeString(s string) string { - // Avoid allocation if we can. - if strings.IndexFunc(s, jsIsSpecial) < 0 { - return s - } - var b bytes.Buffer - JSEscape(&b, []byte(s)) - return b.String() -} - -func jsIsSpecial(rune int) bool { - switch rune { - case '\\', '\'', '"', '<', '>': - return true - } - return rune < ' ' || utf8.RuneSelf <= rune -} - -// JSEscaper returns the escaped JavaScript equivalent of the textual -// representation of its arguments. -func JSEscaper(args ...interface{}) string { - ok := false - var s string - if len(args) == 1 { - s, ok = args[0].(string) - } - if !ok { - s = fmt.Sprint(args...) - } - return JSEscapeString(s) -} - -// URLEscaper returns the escaped value of the textual representation of its -// arguments in a form suitable for embedding in a URL. -func URLEscaper(args ...interface{}) string { - s, ok := "", false - if len(args) == 1 { - s, ok = args[0].(string) - } - if !ok { - s = fmt.Sprint(args...) - } - return http.URLEscape(s) -} diff --git a/src/pkg/exp/template/helper.go b/src/pkg/exp/template/helper.go deleted file mode 100644 index ae6a057ee..000000000 --- a/src/pkg/exp/template/helper.go +++ /dev/null @@ -1,238 +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. - -// Helper functions to make constructing templates and sets easier. - -package template - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" -) - -// Functions and methods to parse a single template. - -// Must is a helper that wraps a call to a function returning (*Template, os.Error) -// and panics if the error is non-nil. It is intended for use in variable initializations -// such as -// var t = template.Must(template.New("name").Parse("text")) -func Must(t *Template, err os.Error) *Template { - if err != nil { - panic(err) - } - return t -} - -// ParseFile creates a new Template and parses the template definition from -// the named file. The template name is the base name of the file. -func ParseFile(filename string) (*Template, os.Error) { - t := New(filepath.Base(filename)) - return t.ParseFile(filename) -} - -// parseFileInSet creates a new Template and parses the template -// definition from the named file. The template name is the base name -// of the file. It also adds the template to the set. Function bindings are -// checked against those in the set. -func parseFileInSet(filename string, set *Set) (*Template, os.Error) { - t := New(filepath.Base(filename)) - return t.parseFileInSet(filename, set) -} - -// ParseFile reads the template definition from a file and parses it to -// construct an internal representation of the template for execution. -// The returned template will be nil if an error occurs. -func (t *Template) ParseFile(filename string) (*Template, os.Error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return t.Parse(string(b)) -} - -// parseFileInSet is the same as ParseFile except that function bindings -// are checked against those in the set and the template is added -// to the set. -// The returned template will be nil if an error occurs. -func (t *Template) parseFileInSet(filename string, set *Set) (*Template, os.Error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return t.ParseInSet(string(b), set) -} - -// Functions and methods to parse a set. - -// SetMust is a helper that wraps a call to a function returning (*Set, os.Error) -// and panics if the error is non-nil. It is intended for use in variable initializations -// such as -// var s = template.SetMust(template.ParseSetFile("file")) -func SetMust(s *Set, err os.Error) *Set { - if err != nil { - panic(err) - } - return s -} - -// ParseFile parses the named files into a set of named templates. -// Each file must be parseable by itself. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseFile(filenames ...string) (*Set, os.Error) { - for _, filename := range filenames { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - _, err = s.Parse(string(b)) - if err != nil { - return nil, err - } - } - return s, nil -} - -// ParseSetFile creates a new Set and parses the set definition from the -// named files. Each file must be individually parseable. -func ParseSetFile(filenames ...string) (*Set, os.Error) { - s := new(Set) - for _, filename := range filenames { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - _, err = s.Parse(string(b)) - if err != nil { - return nil, err - } - } - return s, nil -} - -// ParseFiles parses the set definition from the files identified by the -// pattern. The pattern is processed by filepath.Glob and must match at -// least one file. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseFiles(pattern string) (*Set, os.Error) { - filenames, err := filepath.Glob(pattern) - if err != nil { - return nil, err - } - if len(filenames) == 0 { - return nil, fmt.Errorf("pattern matches no files: %#q", pattern) - } - return s.ParseFile(filenames...) -} - -// ParseSetFiles creates a new Set and parses the set definition from the -// files identified by the pattern. The pattern is processed by filepath.Glob -// and must match at least one file. -func ParseSetFiles(pattern string) (*Set, os.Error) { - set, err := new(Set).ParseFiles(pattern) - if err != nil { - return nil, err - } - return set, nil -} - -// Functions and methods to parse stand-alone template files into a set. - -// ParseTemplateFile parses the named template files and adds -// them to the set. Each template will named the base name of -// its file. -// Unlike with ParseFile, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateFile is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseTemplateFile(filenames ...string) (*Set, os.Error) { - for _, filename := range filenames { - _, err := parseFileInSet(filename, s) - if err != nil { - return nil, err - } - } - return s, nil -} - -// ParseTemplateFiles parses the template files matched by the -// patern and adds them to the set. Each template will named -// the base name of its file. -// Unlike with ParseFiles, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateFiles is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseTemplateFiles(pattern string) (*Set, os.Error) { - filenames, err := filepath.Glob(pattern) - if err != nil { - return nil, err - } - for _, filename := range filenames { - _, err := parseFileInSet(filename, s) - if err != nil { - return nil, err - } - } - return s, nil -} - -// ParseTemplateFile creates a set by parsing the named files, -// each of which defines a single template. Each template will -// named the base name of its file. -// Unlike with ParseFile, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateFile is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. Parsing stops if an error is -// encountered. -func ParseTemplateFile(filenames ...string) (*Set, os.Error) { - set := new(Set) - set.init() - for _, filename := range filenames { - t, err := ParseFile(filename) - if err != nil { - return nil, err - } - if err := set.add(t); err != nil { - return nil, err - } - } - return set, nil -} - -// ParseTemplateFiles creates a set by parsing the files matched -// by the pattern, each of which defines a single template. Each -// template will named the base name of its file. -// Unlike with ParseFiles, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateFiles is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. Parsing stops if an error is -// encountered. -func ParseTemplateFiles(pattern string) (*Set, os.Error) { - set := new(Set) - filenames, err := filepath.Glob(pattern) - if err != nil { - return nil, err - } - for _, filename := range filenames { - t, err := ParseFile(filename) - if err != nil { - return nil, err - } - if err := set.add(t); err != nil { - return nil, err - } - } - return set, nil -} diff --git a/src/pkg/exp/template/html/Makefile b/src/pkg/exp/template/html/Makefile new file mode 100644 index 000000000..2f107da11 --- /dev/null +++ b/src/pkg/exp/template/html/Makefile @@ -0,0 +1,11 @@ +# 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. + +include ../../../../Make.inc + +TARG=exp/template/html +GOFILES=\ + escape.go + +include ../../../../Make.pkg diff --git a/src/pkg/exp/template/html/context.go b/src/pkg/exp/template/html/context.go new file mode 100644 index 000000000..411006883 --- /dev/null +++ b/src/pkg/exp/template/html/context.go @@ -0,0 +1,98 @@ +// 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" +) + +// context describes the state an HTML parser must be in when it reaches the +// portion of HTML produced by evaluating a particular template node. +// +// The zero value of type context is the start context for a template that +// produces an HTML fragment as defined at +// 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 +} + +func (c context) String() string { + return fmt.Sprintf("context{state: %s, delim: %s", c.state, c.delim) +} + +// eq is true if the two contexts are identical field-wise. +func (c context) eq(d context) bool { + return c.state == d.state && c.delim == d.delim +} + +// 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 +//
+// 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 + // this state when its parse position is outside an HTML tag, + // directive, comment, and special element body. + statePCDATA 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 + // 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", +} + +func (s state) String() string { + if uint(s) < uint(len(stateNames)) { + return stateNames[s] + } + return fmt.Sprintf("illegal state %d", uint(s)) +} + +// delim is the delimiter that will end the current HTML attribute. +type delim uint8 + +const ( + // delimNone occurs outside any attribute. + delimNone delim = iota + // delimDoubleQuote occurs when a double quote (") closes the attribute. + delimDoubleQuote + // delimSingleQuote occurs when a single quote (') closes the attribute. + delimSingleQuote + // delimSpaceOrTagEnd occurs when a space or right angle bracket (>) + // closes the attribute. + delimSpaceOrTagEnd +) + +var delimNames = [...]string{ + delimNone: "delimNone", + delimDoubleQuote: "delimDoubleQuote", + delimSingleQuote: "delimSingleQuote", + delimSpaceOrTagEnd: "delimSpaceOrTagEnd", +} + +func (d delim) String() string { + if uint(d) < uint(len(delimNames)) { + return delimNames[d] + } + return fmt.Sprintf("illegal delim %d", uint(d)) +} diff --git a/src/pkg/exp/template/html/escape.go b/src/pkg/exp/template/html/escape.go new file mode 100644 index 000000000..e0e87b98d --- /dev/null +++ b/src/pkg/exp/template/html/escape.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 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 ( + "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) +} + +// escapeNode dispatches to escape helpers by type. +func escapeNode(node parse.Node) { + switch n := node.(type) { + case *parse.ListNode: + escapeListNode(n) + case *parse.TextNode: + // Nothing to do. + case *parse.ActionNode: + escapeActionNode(n) + case *parse.IfNode: + escapeIfNode(n) + case *parse.RangeNode: + escapeRangeNode(n) + case *parse.TemplateNode: + // Nothing to do. + case *parse.WithNode: + escapeWithNode(n) + default: + panic("handling for " + node.String() + " not implemented") + // TODO: Handle other inner node types. + } +} + +// escapeListNode recursively escapes its input's children. +func escapeListNode(node *parse.ListNode) { + if node == nil { + return + } + children := node.Nodes + for _, child := range children { + escapeNode(child) + } +} + +// 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 + + cmds := pipe.Cmds + nCmds := len(cmds) + + // 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 + } + } + } + + htmlEscapeCommand := parse.CommandNode{ + NodeType: parse.NodeCommand, + Args: []parse.Node{parse.NewIdentifier("html")}, + } + + node.Pipe.Cmds = append(node.Pipe.Cmds, &htmlEscapeCommand) +} + +// escapeIfNode recursively escapes the if and then clauses but leaves the +// condition unchanged. +func escapeIfNode(node *parse.IfNode) { + escapeListNode(node.List) + escapeListNode(node.ElseList) +} + +// 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) +} + +// 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) +} diff --git a/src/pkg/exp/template/html/escape_test.go b/src/pkg/exp/template/html/escape_test.go new file mode 100644 index 000000000..345a752a8 --- /dev/null +++ b/src/pkg/exp/template/html/escape_test.go @@ -0,0 +1,75 @@ +// 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" + "testing" +) + +type data struct { + F, T bool + C, G, H string + A, E []string +} + +var testData = data{ + F: false, + T: true, + C: "", + G: "", + H: "", + A: []string{"", ""}, + E: []string{}, +} + +type testCase struct { + name string + input string + output string +} + +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", ``, ``}, +} + +func TestAutoesc(t *testing.T) { + for _, testCase := range testCases { + name := testCase.name + tmpl := template.New(name) + tmpl, err := tmpl.Parse(testCase.input) + if err != nil { + t.Errorf("%s: failed to parse template: %s", name, err) + continue + } + + Escape(tmpl) + + buffer := new(bytes.Buffer) + + err = tmpl.Execute(buffer, testData) + if err != nil { + t.Errorf("%s: template execution failed: %s", name, err) + continue + } + + output := testCase.output + actual := buffer.String() + if output != actual { + t.Errorf("%s: escaped output: %q != %q", + name, output, actual) + } + } +} diff --git a/src/pkg/exp/template/parse.go b/src/pkg/exp/template/parse.go deleted file mode 100644 index 6db00c1c1..000000000 --- a/src/pkg/exp/template/parse.go +++ /dev/null @@ -1,85 +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 template - -import ( - "exp/template/parse" - "os" - "reflect" -) - -// Template is the representation of a parsed template. -type Template struct { - name string - *parse.Tree - // We use two maps, one for parsing and one for execution. - // This separation makes the API cleaner since it doesn't - // expose reflection to the client. - parseFuncs FuncMap - execFuncs map[string]reflect.Value - set *Set // can be nil. -} - -// Name returns the name of the template. -func (t *Template) Name() string { - return t.Tree.Name -} - -// Parsing. - -// New allocates a new template with the given name. -func New(name string) *Template { - return &Template{ - name: name, - parseFuncs: make(FuncMap), - execFuncs: make(map[string]reflect.Value), - } -} - -// Funcs adds the elements of the argument map to the template's function -// map. It panics if a value in the map is not a function with appropriate -// return type. -// The return value is the template, so calls can be chained. -func (t *Template) Funcs(funcMap FuncMap) *Template { - addValueFuncs(t.execFuncs, funcMap) - addFuncs(t.parseFuncs, funcMap) - return t -} - -// Parse parses the template definition string to construct an internal -// representation of the template for execution. -func (t *Template) Parse(s string) (tmpl *Template, err os.Error) { - t.Tree, err = parse.New(t.name).Parse(s, t.parseFuncs, builtins) - if err != nil { - return nil, err - } - return t, nil -} - -// ParseInSet parses the template definition string to construct an internal -// representation of the template for execution. It also adds the template -// to the set. -// Function bindings are checked against those in the set. -func (t *Template) ParseInSet(s string, set *Set) (tmpl *Template, err os.Error) { - var setFuncs FuncMap - if set != nil { - setFuncs = set.parseFuncs - } - t.Tree, err = parse.New(t.name).Parse(s, t.parseFuncs, setFuncs, builtins) - if err != nil { - return nil, err - } - t.addToSet(set) - return t, nil -} - -// addToSet adds the template to the set, verifying it's not being double-assigned. -func (t *Template) addToSet(set *Set) { - if set == nil || t.set == set { - return - } - // If double-assigned, Add will panic and we will turn that into an error. - set.Add(t) -} diff --git a/src/pkg/exp/template/parse/Makefile b/src/pkg/exp/template/parse/Makefile deleted file mode 100644 index 5483a0cf9..000000000 --- a/src/pkg/exp/template/parse/Makefile +++ /dev/null @@ -1,14 +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. - -include ../../../../Make.inc - -TARG=exp/template/parse -GOFILES=\ - lex.go\ - node.go\ - parse.go\ - set.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/exp/template/parse/lex.go b/src/pkg/exp/template/parse/lex.go deleted file mode 100644 index 7ec4e920b..000000000 --- a/src/pkg/exp/template/parse/lex.go +++ /dev/null @@ -1,474 +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 parse - -import ( - "fmt" - "strings" - "unicode" - "utf8" -) - -// item represents a token or text string returned from the scanner. -type item struct { - typ itemType - val string -} - -func (i item) String() string { - switch { - case i.typ == itemEOF: - return "EOF" - case i.typ == itemError: - return i.val - case i.typ > itemKeyword: - return fmt.Sprintf("<%s>", i.val) - case len(i.val) > 10: - return fmt.Sprintf("%.10q...", i.val) - } - return fmt.Sprintf("%q", i.val) -} - -// itemType identifies the type of lex items. -type itemType int - -const ( - itemError itemType = iota // error occurred; value is text of error - itemBool // boolean constant - itemChar // printable ASCII character; grab bag for comma etc. - itemCharConstant // character constant - itemComplex // complex constant (1+2i); imaginary is just a number - itemColonEquals // colon-equals (':=') introducing a declaration - itemEOF - itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y') - itemIdentifier // alphanumeric identifier - itemLeftDelim // left action delimiter - itemNumber // simple number, including imaginary - itemPipe // pipe symbol - itemRawString // raw quoted string (includes quotes) - itemRightDelim // right action delimiter - itemString // quoted string (includes quotes) - itemText // plain text - itemVariable // variable starting with '$', such as '$' or '$1' or '$hello'. - // Keywords appear after all the rest. - itemKeyword // used only to delimit the keywords - itemDot // the cursor, spelled '.'. - itemDefine // define keyword - itemElse // else keyword - itemEnd // end keyword - itemIf // if keyword - itemRange // range keyword - itemTemplate // template keyword - itemWith // with keyword -) - -// Make the types prettyprint. -var itemName = map[itemType]string{ - itemError: "error", - itemBool: "bool", - itemChar: "char", - itemCharConstant: "charconst", - itemComplex: "complex", - itemColonEquals: ":=", - itemEOF: "EOF", - itemField: "field", - itemIdentifier: "identifier", - itemLeftDelim: "left delim", - itemNumber: "number", - itemPipe: "pipe", - itemRawString: "raw string", - itemRightDelim: "right delim", - itemString: "string", - itemVariable: "variable", - // keywords - itemDot: ".", - itemDefine: "define", - itemElse: "else", - itemIf: "if", - itemEnd: "end", - itemRange: "range", - itemTemplate: "template", - itemWith: "with", -} - -func (i itemType) String() string { - s := itemName[i] - if s == "" { - return fmt.Sprintf("item%d", int(i)) - } - return s -} - -var key = map[string]itemType{ - ".": itemDot, - "define": itemDefine, - "else": itemElse, - "end": itemEnd, - "if": itemIf, - "range": itemRange, - "template": itemTemplate, - "with": itemWith, -} - -const eof = -1 - -// stateFn represents the state of the scanner as a function that returns the next state. -type stateFn func(*lexer) stateFn - -// lexer holds the state of the scanner. -type lexer struct { - name string // the name of the input; used only for error reports. - input string // the string being scanned. - state stateFn // the next lexing function to enter - pos int // current position in the input. - start int // start position of this item. - width int // width of last rune read from input. - items chan item // channel of scanned items. -} - -// next returns the next rune in the input. -func (l *lexer) next() (rune int) { - if l.pos >= len(l.input) { - l.width = 0 - return eof - } - rune, l.width = utf8.DecodeRuneInString(l.input[l.pos:]) - l.pos += l.width - return rune -} - -// peek returns but does not consume the next rune in the input. -func (l *lexer) peek() int { - rune := l.next() - l.backup() - return rune -} - -// backup steps back one rune. Can only be called once per call of next. -func (l *lexer) backup() { - l.pos -= l.width -} - -// emit passes an item back to the client. -func (l *lexer) emit(t itemType) { - l.items <- item{t, l.input[l.start:l.pos]} - l.start = l.pos -} - -// ignore skips over the pending input before this point. -func (l *lexer) ignore() { - l.start = l.pos -} - -// accept consumes the next rune if it's from the valid set. -func (l *lexer) accept(valid string) bool { - if strings.IndexRune(valid, l.next()) >= 0 { - return true - } - l.backup() - return false -} - -// acceptRun consumes a run of runes from the valid set. -func (l *lexer) acceptRun(valid string) { - for strings.IndexRune(valid, l.next()) >= 0 { - } - l.backup() -} - -// lineNumber reports which line we're on. Doing it this way -// means we don't have to worry about peek double counting. -func (l *lexer) lineNumber() int { - return 1 + strings.Count(l.input[:l.pos], "\n") -} - -// error returns an error token and terminates the scan by passing -// back a nil pointer that will be the next state, terminating l.run. -func (l *lexer) errorf(format string, args ...interface{}) stateFn { - l.items <- item{itemError, fmt.Sprintf(format, args...)} - return nil -} - -// nextItem returns the next item from the input. -func (l *lexer) nextItem() item { - for { - select { - case item := <-l.items: - return item - default: - l.state = l.state(l) - } - } - panic("not reached") -} - -// lex creates a new scanner for the input string. -func lex(name, input string) *lexer { - l := &lexer{ - name: name, - input: input, - state: lexText, - items: make(chan item, 2), // Two items of buffering is sufficient for all state functions - } - return l -} - -// state functions - -const ( - leftDelim = "{{" - rightDelim = "}}" - leftComment = "{{/*" - rightComment = "*/}}" -) - -// lexText scans until an opening action delimiter, "{{". -func lexText(l *lexer) stateFn { - for { - if strings.HasPrefix(l.input[l.pos:], leftDelim) { - if l.pos > l.start { - l.emit(itemText) - } - return lexLeftDelim - } - if l.next() == eof { - break - } - } - // Correctly reached EOF. - if l.pos > l.start { - l.emit(itemText) - } - l.emit(itemEOF) - return nil -} - -// lexLeftDelim scans the left delimiter, which is known to be present. -func lexLeftDelim(l *lexer) stateFn { - if strings.HasPrefix(l.input[l.pos:], leftComment) { - return lexComment - } - l.pos += len(leftDelim) - l.emit(itemLeftDelim) - return lexInsideAction -} - -// lexComment scans a comment. The left comment marker is known to be present. -func lexComment(l *lexer) stateFn { - i := strings.Index(l.input[l.pos:], rightComment) - if i < 0 { - return l.errorf("unclosed comment") - } - l.pos += i + len(rightComment) - l.ignore() - return lexText -} - -// lexRightDelim scans the right delimiter, which is known to be present. -func lexRightDelim(l *lexer) stateFn { - l.pos += len(rightDelim) - l.emit(itemRightDelim) - return lexText -} - -// lexInsideAction scans the elements inside action delimiters. -func lexInsideAction(l *lexer) stateFn { - // Either number, quoted string, or identifier. - // Spaces separate and are ignored. - // Pipe symbols separate and are emitted. - for { - if strings.HasPrefix(l.input[l.pos:], rightDelim) { - return lexRightDelim - } - switch r := l.next(); { - case r == eof || r == '\n': - return l.errorf("unclosed action") - case isSpace(r): - l.ignore() - case r == ':': - if l.next() != '=' { - return l.errorf("expected :=") - } - l.emit(itemColonEquals) - case r == '|': - l.emit(itemPipe) - case r == '"': - return lexQuote - case r == '`': - return lexRawQuote - case r == '$': - return lexIdentifier - case r == '\'': - return lexChar - case r == '.': - // special look-ahead for ".field" so we don't break l.backup(). - if l.pos < len(l.input) { - r := l.input[l.pos] - if r < '0' || '9' < r { - return lexIdentifier // itemDot comes from the keyword table. - } - } - fallthrough // '.' can start a number. - case r == '+' || r == '-' || ('0' <= r && r <= '9'): - l.backup() - return lexNumber - case isAlphaNumeric(r): - l.backup() - return lexIdentifier - case r <= unicode.MaxASCII && unicode.IsPrint(r): - l.emit(itemChar) - return lexInsideAction - default: - return l.errorf("unrecognized character in action: %#U", r) - } - } - return nil -} - -// lexIdentifier scans an alphanumeric or field. -func lexIdentifier(l *lexer) stateFn { -Loop: - for { - switch r := l.next(); { - case isAlphaNumeric(r): - // absorb. - case r == '.' && (l.input[l.start] == '.' || l.input[l.start] == '$'): - // field chaining; absorb into one token. - default: - l.backup() - word := l.input[l.start:l.pos] - switch { - case key[word] > itemKeyword: - l.emit(key[word]) - case word[0] == '.': - l.emit(itemField) - case word[0] == '$': - l.emit(itemVariable) - case word == "true", word == "false": - l.emit(itemBool) - default: - l.emit(itemIdentifier) - } - break Loop - } - } - return lexInsideAction -} - -// lexChar scans a character constant. The initial quote is already -// scanned. Syntax checking is done by the parse. -func lexChar(l *lexer) stateFn { -Loop: - for { - switch l.next() { - case '\\': - if r := l.next(); r != eof && r != '\n' { - break - } - fallthrough - case eof, '\n': - return l.errorf("unterminated character constant") - case '\'': - break Loop - } - } - l.emit(itemCharConstant) - return lexInsideAction -} - -// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This -// isn't a perfect number scanner - for instance it accepts "." and "0x0.2" -// and "089" - but when it's wrong the input is invalid and the parser (via -// strconv) will notice. -func lexNumber(l *lexer) stateFn { - if !l.scanNumber() { - return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) - } - if sign := l.peek(); sign == '+' || sign == '-' { - // Complex: 1+2i. No spaces, must end in 'i'. - if !l.scanNumber() || l.input[l.pos-1] != 'i' { - return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) - } - l.emit(itemComplex) - } else { - l.emit(itemNumber) - } - return lexInsideAction -} - -func (l *lexer) scanNumber() bool { - // Optional leading sign. - l.accept("+-") - // Is it hex? - digits := "0123456789" - if l.accept("0") && l.accept("xX") { - digits = "0123456789abcdefABCDEF" - } - l.acceptRun(digits) - if l.accept(".") { - l.acceptRun(digits) - } - if l.accept("eE") { - l.accept("+-") - l.acceptRun("0123456789") - } - // Is it imaginary? - l.accept("i") - // Next thing mustn't be alphanumeric. - if isAlphaNumeric(l.peek()) { - l.next() - return false - } - return true -} - -// lexQuote scans a quoted string. -func lexQuote(l *lexer) stateFn { -Loop: - for { - switch l.next() { - case '\\': - if r := l.next(); r != eof && r != '\n' { - break - } - fallthrough - case eof, '\n': - return l.errorf("unterminated quoted string") - case '"': - break Loop - } - } - l.emit(itemString) - return lexInsideAction -} - -// lexRawQuote scans a raw quoted string. -func lexRawQuote(l *lexer) stateFn { -Loop: - for { - switch l.next() { - case eof, '\n': - return l.errorf("unterminated raw quoted string") - case '`': - break Loop - } - } - l.emit(itemRawString) - return lexInsideAction -} - -// isSpace reports whether r is a space character. -func isSpace(r int) bool { - switch r { - case ' ', '\t', '\n', '\r': - return true - } - return false -} - -// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. -func isAlphaNumeric(r int) bool { - return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) -} diff --git a/src/pkg/exp/template/parse/lex_test.go b/src/pkg/exp/template/parse/lex_test.go deleted file mode 100644 index 2ad91d5fa..000000000 --- a/src/pkg/exp/template/parse/lex_test.go +++ /dev/null @@ -1,209 +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 parse - -import ( - "reflect" - "testing" -) - -type lexTest struct { - name string - input string - items []item -} - -var ( - tEOF = item{itemEOF, ""} - tLeft = item{itemLeftDelim, "{{"} - tRight = item{itemRightDelim, "}}"} - tRange = item{itemRange, "range"} - tPipe = item{itemPipe, "|"} - tFor = item{itemIdentifier, "for"} - tQuote = item{itemString, `"abc \n\t\" "`} - raw = "`" + `abc\n\t\" ` + "`" - tRawQuote = item{itemRawString, raw} -) - -var lexTests = []lexTest{ - {"empty", "", []item{tEOF}}, - {"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}}, - {"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}}, - {"text with comment", "hello-{{/* this is a comment */}}-world", []item{ - {itemText, "hello-"}, - {itemText, "-world"}, - tEOF, - }}, - {"punctuation", "{{,@%}}", []item{ - tLeft, - {itemChar, ","}, - {itemChar, "@"}, - {itemChar, "%"}, - tRight, - tEOF, - }}, - {"empty action", `{{}}`, []item{tLeft, tRight, tEOF}}, - {"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}}, - {"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}}, - {"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}}, - {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{ - tLeft, - {itemNumber, "1"}, - {itemNumber, "02"}, - {itemNumber, "0x14"}, - {itemNumber, "-7.2i"}, - {itemNumber, "1e3"}, - {itemNumber, "+1.2e-4"}, - {itemNumber, "4.2i"}, - {itemComplex, "1+2i"}, - tRight, - tEOF, - }}, - {"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{ - tLeft, - {itemCharConstant, `'a'`}, - {itemCharConstant, `'\n'`}, - {itemCharConstant, `'\''`}, - {itemCharConstant, `'\\'`}, - {itemCharConstant, `'\u00FF'`}, - {itemCharConstant, `'\xFF'`}, - {itemCharConstant, `'本'`}, - tRight, - tEOF, - }}, - {"bools", "{{true false}}", []item{ - tLeft, - {itemBool, "true"}, - {itemBool, "false"}, - tRight, - tEOF, - }}, - {"dot", "{{.}}", []item{ - tLeft, - {itemDot, "."}, - tRight, - tEOF, - }}, - {"dots", "{{.x . .2 .x.y}}", []item{ - tLeft, - {itemField, ".x"}, - {itemDot, "."}, - {itemNumber, ".2"}, - {itemField, ".x.y"}, - tRight, - tEOF, - }}, - {"keywords", "{{range if else end with}}", []item{ - tLeft, - {itemRange, "range"}, - {itemIf, "if"}, - {itemElse, "else"}, - {itemEnd, "end"}, - {itemWith, "with"}, - tRight, - tEOF, - }}, - {"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{ - tLeft, - {itemVariable, "$c"}, - {itemColonEquals, ":="}, - {itemIdentifier, "printf"}, - {itemVariable, "$"}, - {itemVariable, "$hello"}, - {itemVariable, "$23"}, - {itemVariable, "$"}, - {itemVariable, "$var.Field"}, - {itemField, ".Method"}, - tRight, - tEOF, - }}, - {"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{ - {itemText, "intro "}, - tLeft, - {itemIdentifier, "echo"}, - {itemIdentifier, "hi"}, - {itemNumber, "1.2"}, - tPipe, - {itemIdentifier, "noargs"}, - tPipe, - {itemIdentifier, "args"}, - {itemNumber, "1"}, - {itemString, `"hi"`}, - tRight, - {itemText, " outro"}, - tEOF, - }}, - {"declaration", "{{$v := 3}}", []item{ - tLeft, - {itemVariable, "$v"}, - {itemColonEquals, ":="}, - {itemNumber, "3"}, - tRight, - tEOF, - }}, - {"2 declarations", "{{$v , $w := 3}}", []item{ - tLeft, - {itemVariable, "$v"}, - {itemChar, ","}, - {itemVariable, "$w"}, - {itemColonEquals, ":="}, - {itemNumber, "3"}, - tRight, - tEOF, - }}, - // errors - {"badchar", "#{{\x01}}", []item{ - {itemText, "#"}, - tLeft, - {itemError, "unrecognized character in action: U+0001"}, - }}, - {"unclosed action", "{{\n}}", []item{ - tLeft, - {itemError, "unclosed action"}, - }}, - {"EOF in action", "{{range", []item{ - tLeft, - tRange, - {itemError, "unclosed action"}, - }}, - {"unclosed quote", "{{\"\n\"}}", []item{ - tLeft, - {itemError, "unterminated quoted string"}, - }}, - {"unclosed raw quote", "{{`xx\n`}}", []item{ - tLeft, - {itemError, "unterminated raw quoted string"}, - }}, - {"unclosed char constant", "{{'\n}}", []item{ - tLeft, - {itemError, "unterminated character constant"}, - }}, - {"bad number", "{{3k}}", []item{ - tLeft, - {itemError, `bad number syntax: "3k"`}, - }}, -} - -// collect gathers the emitted items into a slice. -func collect(t *lexTest) (items []item) { - l := lex(t.name, t.input) - for { - item := l.nextItem() - items = append(items, item) - if item.typ == itemEOF || item.typ == itemError { - break - } - } - return -} - -func TestLex(t *testing.T) { - for _, test := range lexTests { - items := collect(&test) - if !reflect.DeepEqual(items, test.items) { - t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) - } - } -} diff --git a/src/pkg/exp/template/parse/node.go b/src/pkg/exp/template/parse/node.go deleted file mode 100644 index 0f77ad850..000000000 --- a/src/pkg/exp/template/parse/node.go +++ /dev/null @@ -1,468 +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. - -// Parse nodes. - -package parse - -import ( - "bytes" - "fmt" - "os" - "strconv" - "strings" -) - -// A node is an element in the parse tree. The interface is trivial. -type Node interface { - Type() NodeType - String() string -} - -// NodeType identifies the type of a parse tree node. -type NodeType int - -// Type returns itself and provides an easy default implementation -// for embedding in a Node. Embedded in all non-trivial Nodes. -func (t NodeType) Type() NodeType { - return t -} - -const ( - NodeText NodeType = iota // Plain text. - NodeAction // An simple action such as field evaluation. - NodeBool // A boolean constant. - NodeCommand // An element of a pipeline. - NodeDot // The cursor, dot. - NodeElse // An else action. - NodeEnd // An end action. - NodeField // A field or method name. - NodeIdentifier // A identifier; always a function name. - NodeIf // An if action. - NodeList // A list of Nodes. - NodeNumber // A numerical constant. - NodePipe // A pipeline of commands. - NodeRange // A range action. - NodeString // A string constant. - NodeTemplate // A template invocation action. - NodeVariable // A $ variable. - NodeWith // A with action. -) - -// Nodes. - -// ListNode holds a sequence of nodes. -type ListNode struct { - NodeType - Nodes []Node // The element nodes in lexical order. -} - -func newList() *ListNode { - return &ListNode{NodeType: NodeList} -} - -func (l *ListNode) append(n Node) { - l.Nodes = append(l.Nodes, n) -} - -func (l *ListNode) String() string { - b := new(bytes.Buffer) - fmt.Fprint(b, "[") - for _, n := range l.Nodes { - fmt.Fprint(b, n) - } - fmt.Fprint(b, "]") - return b.String() -} - -// TextNode holds plain text. -type TextNode struct { - NodeType - Text []byte // The text; may span newlines. -} - -func newText(text string) *TextNode { - return &TextNode{NodeType: NodeText, Text: []byte(text)} -} - -func (t *TextNode) String() string { - return fmt.Sprintf("(text: %q)", t.Text) -} - -// PipeNode holds a pipeline with optional declaration -type PipeNode struct { - NodeType - Line int // The line number in the input. - Decl []*VariableNode // Variable declarations in lexical order. - Cmds []*CommandNode // The commands in lexical order. -} - -func newPipeline(line int, decl []*VariableNode) *PipeNode { - return &PipeNode{NodeType: NodePipe, Line: line, Decl: decl} -} - -func (p *PipeNode) append(command *CommandNode) { - p.Cmds = append(p.Cmds, command) -} - -func (p *PipeNode) String() string { - if p.Decl != nil { - return fmt.Sprintf("%v := %v", p.Decl, p.Cmds) - } - return fmt.Sprintf("%v", p.Cmds) -} - -// ActionNode holds an action (something bounded by delimiters). -// Control actions have their own nodes; ActionNode represents simple -// ones such as field evaluations. -type ActionNode struct { - NodeType - Line int // The line number in the input. - Pipe *PipeNode // The pipeline in the action. -} - -func newAction(line int, pipe *PipeNode) *ActionNode { - return &ActionNode{NodeType: NodeAction, Line: line, Pipe: pipe} -} - -func (a *ActionNode) String() string { - return fmt.Sprintf("(action: %v)", a.Pipe) -} - -// CommandNode holds a command (a pipeline inside an evaluating action). -type CommandNode struct { - NodeType - Args []Node // Arguments in lexical order: Identifier, field, or constant. -} - -func newCommand() *CommandNode { - return &CommandNode{NodeType: NodeCommand} -} - -func (c *CommandNode) append(arg Node) { - c.Args = append(c.Args, arg) -} - -func (c *CommandNode) String() string { - return fmt.Sprintf("(command: %v)", c.Args) -} - -// IdentifierNode holds an identifier. -type IdentifierNode struct { - NodeType - Ident string // The identifier's name. -} - -func newIdentifier(ident string) *IdentifierNode { - return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident} -} - -func (i *IdentifierNode) String() string { - return fmt.Sprintf("I=%s", i.Ident) -} - -// VariableNode holds a list of variable names. The dollar sign is -// part of the name. -type VariableNode struct { - NodeType - Ident []string // Variable names in lexical order. -} - -func newVariable(ident string) *VariableNode { - return &VariableNode{NodeType: NodeVariable, Ident: strings.Split(ident, ".")} -} - -func (v *VariableNode) String() string { - return fmt.Sprintf("V=%s", v.Ident) -} - -// DotNode holds the special identifier '.'. It is represented by a nil pointer. -type DotNode bool - -func newDot() *DotNode { - return nil -} - -func (d *DotNode) Type() NodeType { - return NodeDot -} - -func (d *DotNode) String() string { - return "{{<.>}}" -} - -// FieldNode holds a field (identifier starting with '.'). -// The names may be chained ('.x.y'). -// The period is dropped from each ident. -type FieldNode struct { - NodeType - Ident []string // The identifiers in lexical order. -} - -func newField(ident string) *FieldNode { - return &FieldNode{NodeType: NodeField, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period -} - -func (f *FieldNode) String() string { - return fmt.Sprintf("F=%s", f.Ident) -} - -// BoolNode holds a boolean constant. -type BoolNode struct { - NodeType - True bool // The value of the boolean constant. -} - -func newBool(true bool) *BoolNode { - return &BoolNode{NodeType: NodeBool, True: true} -} - -func (b *BoolNode) String() string { - return fmt.Sprintf("B=%t", b.True) -} - -// NumberNode holds a number: signed or unsigned integer, float, or complex. -// The value is parsed and stored under all the types that can represent the value. -// This simulates in a small amount of code the behavior of Go's ideal constants. -type NumberNode struct { - NodeType - IsInt bool // Number has an integral value. - IsUint bool // Number has an unsigned integral value. - IsFloat bool // Number has a floating-point value. - IsComplex bool // Number is complex. - Int64 int64 // The signed integer value. - Uint64 uint64 // The unsigned integer value. - Float64 float64 // The floating-point value. - Complex128 complex128 // The complex value. - Text string // The original textual representation from the input. -} - -func newNumber(text string, typ itemType) (*NumberNode, os.Error) { - n := &NumberNode{NodeType: NodeNumber, Text: text} - switch typ { - case itemCharConstant: - rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0]) - if err != nil { - return nil, err - } - if tail != "'" { - return nil, fmt.Errorf("malformed character constant: %s", text) - } - n.Int64 = int64(rune) - n.IsInt = true - n.Uint64 = uint64(rune) - n.IsUint = true - n.Float64 = float64(rune) // odd but those are the rules. - n.IsFloat = true - return n, nil - case itemComplex: - // fmt.Sscan can parse the pair, so let it do the work. - if _, err := fmt.Sscan(text, &n.Complex128); err != nil { - return nil, err - } - n.IsComplex = true - n.simplifyComplex() - return n, nil - } - // Imaginary constants can only be complex unless they are zero. - if len(text) > 0 && text[len(text)-1] == 'i' { - f, err := strconv.Atof64(text[:len(text)-1]) - if err == nil { - n.IsComplex = true - n.Complex128 = complex(0, f) - n.simplifyComplex() - return n, nil - } - } - // Do integer test first so we get 0x123 etc. - u, err := strconv.Btoui64(text, 0) // will fail for -0; fixed below. - if err == nil { - n.IsUint = true - n.Uint64 = u - } - i, err := strconv.Btoi64(text, 0) - if err == nil { - n.IsInt = true - n.Int64 = i - if i == 0 { - n.IsUint = true // in case of -0. - n.Uint64 = u - } - } - // If an integer extraction succeeded, promote the float. - if n.IsInt { - n.IsFloat = true - n.Float64 = float64(n.Int64) - } else if n.IsUint { - n.IsFloat = true - n.Float64 = float64(n.Uint64) - } else { - f, err := strconv.Atof64(text) - if err == nil { - n.IsFloat = true - n.Float64 = f - // If a floating-point extraction succeeded, extract the int if needed. - if !n.IsInt && float64(int64(f)) == f { - n.IsInt = true - n.Int64 = int64(f) - } - if !n.IsUint && float64(uint64(f)) == f { - n.IsUint = true - n.Uint64 = uint64(f) - } - } - } - if !n.IsInt && !n.IsUint && !n.IsFloat { - return nil, fmt.Errorf("illegal number syntax: %q", text) - } - return n, nil -} - -// simplifyComplex pulls out any other types that are represented by the complex number. -// These all require that the imaginary part be zero. -func (n *NumberNode) simplifyComplex() { - n.IsFloat = imag(n.Complex128) == 0 - if n.IsFloat { - n.Float64 = real(n.Complex128) - n.IsInt = float64(int64(n.Float64)) == n.Float64 - if n.IsInt { - n.Int64 = int64(n.Float64) - } - n.IsUint = float64(uint64(n.Float64)) == n.Float64 - if n.IsUint { - n.Uint64 = uint64(n.Float64) - } - } -} - -func (n *NumberNode) String() string { - return fmt.Sprintf("N=%s", n.Text) -} - -// StringNode holds a string constant. The value has been "unquoted". -type StringNode struct { - NodeType - Quoted string // The original text of the string, with quotes. - Text string // The string, after quote processing. -} - -func newString(orig, text string) *StringNode { - return &StringNode{NodeType: NodeString, Quoted: orig, Text: text} -} - -func (s *StringNode) String() string { - return fmt.Sprintf("S=%#q", s.Text) -} - -// EndNode represents an {{end}} action. It is represented by a nil pointer. -type EndNode bool - -func newEnd() *EndNode { - return nil -} - -func (e *EndNode) Type() NodeType { - return NodeEnd -} - -func (e *EndNode) String() string { - return "{{end}}" -} - -// ElseNode represents an {{else}} action. -type ElseNode struct { - NodeType - Line int // The line number in the input. -} - -func newElse(line int) *ElseNode { - return &ElseNode{NodeType: NodeElse, Line: line} -} - -func (e *ElseNode) Type() NodeType { - return NodeElse -} - -func (e *ElseNode) String() string { - return "{{else}}" -} - -// IfNode represents an {{if}} action and its commands. -type IfNode struct { - NodeType - Line int // The line number in the input. - Pipe *PipeNode // The pipeline to be evaluated. - List *ListNode // What to execute if the value is non-empty. - ElseList *ListNode // What to execute if the value is empty (nil if absent). -} - -func newIf(line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { - return &IfNode{NodeType: NodeIf, Line: line, Pipe: pipe, List: list, ElseList: elseList} -} - -func (i *IfNode) String() string { - if i.ElseList != nil { - return fmt.Sprintf("({{if %s}} %s {{else}} %s)", i.Pipe, i.List, i.ElseList) - } - return fmt.Sprintf("({{if %s}} %s)", i.Pipe, i.List) -} - -// RangeNode represents a {{range}} action and its commands. -type RangeNode struct { - NodeType - Line int // The line number in the input. - Pipe *PipeNode // The pipeline to be evaluated. - List *ListNode // What to execute if the value is non-empty. - ElseList *ListNode // What to execute if the value is empty (nil if absent). -} - -func newRange(line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { - return &RangeNode{NodeType: NodeRange, Line: line, Pipe: pipe, List: list, ElseList: elseList} -} - -func (r *RangeNode) String() string { - if r.ElseList != nil { - return fmt.Sprintf("({{range %s}} %s {{else}} %s)", r.Pipe, r.List, r.ElseList) - } - return fmt.Sprintf("({{range %s}} %s)", r.Pipe, r.List) -} - -// TemplateNode represents a {{template}} action. -type TemplateNode struct { - NodeType - Line int // The line number in the input. - Name string // The name of the template (unquoted). - Pipe *PipeNode // The command to evaluate as dot for the template. -} - -func newTemplate(line int, name string, pipe *PipeNode) *TemplateNode { - return &TemplateNode{NodeType: NodeTemplate, Line: line, Name: name, Pipe: pipe} -} - -func (t *TemplateNode) String() string { - if t.Pipe == nil { - return fmt.Sprintf("{{template %q}}", t.Name) - } - return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe) -} - -// WithNode represents a {{with}} action and its commands. -type WithNode struct { - NodeType - Line int // The line number in the input. - Pipe *PipeNode // The pipeline to be evaluated. - List *ListNode // What to execute if the value is non-empty. - ElseList *ListNode // What to execute if the value is empty (nil if absent). -} - -func newWith(line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { - return &WithNode{NodeType: NodeWith, Line: line, Pipe: pipe, List: list, ElseList: elseList} -} - -func (w *WithNode) String() string { - if w.ElseList != nil { - return fmt.Sprintf("({{with %s}} %s {{else}} %s)", w.Pipe, w.List, w.ElseList) - } - return fmt.Sprintf("({{with %s}} %s)", w.Pipe, w.List) -} diff --git a/src/pkg/exp/template/parse/parse.go b/src/pkg/exp/template/parse/parse.go deleted file mode 100644 index f8f9023e5..000000000 --- a/src/pkg/exp/template/parse/parse.go +++ /dev/null @@ -1,436 +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 parse builds parse trees for templates. The grammar is defined -// in the documents for the exp/template package. -package parse - -import ( - "fmt" - "os" - "runtime" - "strconv" - "unicode" -) - -// Tree is the representation of a parsed template. -type Tree struct { - Name string // Name is the name of the template. - Root *ListNode // Root is the top-level root of the parse tree. - // Parsing only; cleared after parse. - funcs []map[string]interface{} - lex *lexer - token [2]item // two-token lookahead for parser. - peekCount int - vars []string // variables defined at the moment. -} - -// next returns the next token. -func (t *Tree) next() item { - if t.peekCount > 0 { - t.peekCount-- - } else { - t.token[0] = t.lex.nextItem() - } - return t.token[t.peekCount] -} - -// backup backs the input stream up one token. -func (t *Tree) backup() { - t.peekCount++ -} - -// backup2 backs the input stream up two tokens -func (t *Tree) backup2(t1 item) { - t.token[1] = t1 - t.peekCount = 2 -} - -// peek returns but does not consume the next token. -func (t *Tree) peek() item { - if t.peekCount > 0 { - return t.token[t.peekCount-1] - } - t.peekCount = 1 - t.token[0] = t.lex.nextItem() - return t.token[0] -} - -// Parsing. - -// New allocates a new template with the given name. -func New(name string, funcs ...map[string]interface{}) *Tree { - return &Tree{ - Name: name, - funcs: funcs, - } -} - -// errorf formats the error and terminates processing. -func (t *Tree) errorf(format string, args ...interface{}) { - t.Root = nil - format = fmt.Sprintf("template: %s:%d: %s", t.Name, t.lex.lineNumber(), format) - panic(fmt.Errorf(format, args...)) -} - -// error terminates processing. -func (t *Tree) error(err os.Error) { - t.errorf("%s", err) -} - -// expect consumes the next token and guarantees it has the required type. -func (t *Tree) expect(expected itemType, context string) item { - token := t.next() - if token.typ != expected { - t.errorf("expected %s in %s; got %s", expected, context, token) - } - return token -} - -// unexpected complains about the token and terminates processing. -func (t *Tree) unexpected(token item, context string) { - t.errorf("unexpected %s in %s", token, context) -} - -// recover is the handler that turns panics into returns from the top level of Parse. -func (t *Tree) recover(errp *os.Error) { - e := recover() - if e != nil { - if _, ok := e.(runtime.Error); ok { - panic(e) - } - if t != nil { - t.stopParse() - } - *errp = e.(os.Error) - } - return -} - -// startParse starts the template parsing from the lexer. -func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) { - t.Root = nil - t.lex = lex - t.vars = []string{"$"} - t.funcs = funcs -} - -// stopParse terminates parsing. -func (t *Tree) stopParse() { - t.lex = nil - t.vars = nil - t.funcs = nil -} - -// atEOF returns true if, possibly after spaces, we're at EOF. -func (t *Tree) atEOF() bool { - for { - token := t.peek() - switch token.typ { - case itemEOF: - return true - case itemText: - for _, r := range token.val { - if !unicode.IsSpace(r) { - return false - } - } - t.next() // skip spaces. - continue - } - break - } - return false -} - -// Parse parses the template definition string to construct an internal -// representation of the template for execution. -func (t *Tree) Parse(s string, funcs ...map[string]interface{}) (tree *Tree, err os.Error) { - defer t.recover(&err) - t.startParse(funcs, lex(t.Name, s)) - t.parse(true) - t.stopParse() - return t, nil -} - -// parse is the helper for Parse. -// It triggers an error if we expect EOF but don't reach it. -func (t *Tree) parse(toEOF bool) (next Node) { - t.Root, next = t.itemList(true) - if toEOF && next != nil { - t.errorf("unexpected %s", next) - } - return next -} - -// itemList: -// textOrAction* -// Terminates at EOF and at {{end}} or {{else}}, which is returned separately. -// The toEOF flag tells whether we expect to reach EOF. -func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) { - list = newList() - for t.peek().typ != itemEOF { - n := t.textOrAction() - switch n.Type() { - case NodeEnd, NodeElse: - return list, n - } - list.append(n) - } - if !toEOF { - t.unexpected(t.next(), "input") - } - return list, nil -} - -// textOrAction: -// text | action -func (t *Tree) textOrAction() Node { - switch token := t.next(); token.typ { - case itemText: - return newText(token.val) - case itemLeftDelim: - return t.action() - default: - t.unexpected(token, "input") - } - return nil -} - -// Action: -// control -// command ("|" command)* -// Left delim is past. Now get actions. -// First word could be a keyword such as range. -func (t *Tree) action() (n Node) { - switch token := t.next(); token.typ { - case itemElse: - return t.elseControl() - case itemEnd: - return t.endControl() - case itemIf: - return t.ifControl() - case itemRange: - return t.rangeControl() - case itemTemplate: - return t.templateControl() - case itemWith: - return t.withControl() - } - t.backup() - // Do not pop variables; they persist until "end". - return newAction(t.lex.lineNumber(), t.pipeline("command")) -} - -// Pipeline: -// field or command -// pipeline "|" pipeline -func (t *Tree) pipeline(context string) (pipe *PipeNode) { - var decl []*VariableNode - // Are there declarations? - for { - if v := t.peek(); v.typ == itemVariable { - t.next() - if next := t.peek(); next.typ == itemColonEquals || next.typ == itemChar { - t.next() - variable := newVariable(v.val) - if len(variable.Ident) != 1 { - t.errorf("illegal variable in declaration: %s", v.val) - } - decl = append(decl, variable) - t.vars = append(t.vars, v.val) - if next.typ == itemChar && next.val == "," { - if context == "range" && len(decl) < 2 { - continue - } - t.errorf("too many declarations in %s", context) - } - } else { - t.backup2(v) - } - } - break - } - pipe = newPipeline(t.lex.lineNumber(), decl) - for { - switch token := t.next(); token.typ { - case itemRightDelim: - if len(pipe.Cmds) == 0 { - t.errorf("missing value for %s", context) - } - return - case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier, - itemVariable, itemNumber, itemRawString, itemString: - t.backup() - pipe.append(t.command()) - default: - t.unexpected(token, context) - } - } - return -} - -func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list, elseList *ListNode) { - lineNum = t.lex.lineNumber() - defer t.popVars(len(t.vars)) - pipe = t.pipeline(context) - var next Node - list, next = t.itemList(false) - switch next.Type() { - case NodeEnd: //done - case NodeElse: - elseList, next = t.itemList(false) - if next.Type() != NodeEnd { - t.errorf("expected end; found %s", next) - } - elseList = elseList - } - return lineNum, pipe, list, elseList -} - -// If: -// {{if pipeline}} itemList {{end}} -// {{if pipeline}} itemList {{else}} itemList {{end}} -// If keyword is past. -func (t *Tree) ifControl() Node { - return newIf(t.parseControl("if")) -} - -// Range: -// {{range pipeline}} itemList {{end}} -// {{range pipeline}} itemList {{else}} itemList {{end}} -// Range keyword is past. -func (t *Tree) rangeControl() Node { - return newRange(t.parseControl("range")) -} - -// With: -// {{with pipeline}} itemList {{end}} -// {{with pipeline}} itemList {{else}} itemList {{end}} -// If keyword is past. -func (t *Tree) withControl() Node { - return newWith(t.parseControl("with")) -} - -// End: -// {{end}} -// End keyword is past. -func (t *Tree) endControl() Node { - t.expect(itemRightDelim, "end") - return newEnd() -} - -// Else: -// {{else}} -// Else keyword is past. -func (t *Tree) elseControl() Node { - t.expect(itemRightDelim, "else") - return newElse(t.lex.lineNumber()) -} - -// Template: -// {{template stringValue pipeline}} -// Template keyword is past. The name must be something that can evaluate -// to a string. -func (t *Tree) templateControl() Node { - var name string - switch token := t.next(); token.typ { - case itemString, itemRawString: - s, err := strconv.Unquote(token.val) - if err != nil { - t.error(err) - } - name = s - default: - t.unexpected(token, "template invocation") - } - var pipe *PipeNode - if t.next().typ != itemRightDelim { - t.backup() - // Do not pop variables; they persist until "end". - pipe = t.pipeline("template") - } - return newTemplate(t.lex.lineNumber(), name, pipe) -} - -// command: -// space-separated arguments up to a pipeline character or right delimiter. -// we consume the pipe character but leave the right delim to terminate the action. -func (t *Tree) command() *CommandNode { - cmd := newCommand() -Loop: - for { - switch token := t.next(); token.typ { - case itemRightDelim: - t.backup() - break Loop - case itemPipe: - break Loop - case itemError: - t.errorf("%s", token.val) - case itemIdentifier: - if !t.hasFunction(token.val) { - t.errorf("function %q not defined", token.val) - } - cmd.append(newIdentifier(token.val)) - case itemDot: - cmd.append(newDot()) - case itemVariable: - cmd.append(t.useVar(token.val)) - case itemField: - cmd.append(newField(token.val)) - case itemBool: - cmd.append(newBool(token.val == "true")) - case itemCharConstant, itemComplex, itemNumber: - number, err := newNumber(token.val, token.typ) - if err != nil { - t.error(err) - } - cmd.append(number) - case itemString, itemRawString: - s, err := strconv.Unquote(token.val) - if err != nil { - t.error(err) - } - cmd.append(newString(token.val, s)) - default: - t.unexpected(token, "command") - } - } - if len(cmd.Args) == 0 { - t.errorf("empty command") - } - return cmd -} - -// hasFunction reports if a function name exists in the Tree's maps. -func (t *Tree) hasFunction(name string) bool { - for _, funcMap := range t.funcs { - if funcMap == nil { - continue - } - if funcMap[name] != nil { - return true - } - } - return false -} - -// popVars trims the variable list to the specified length -func (t *Tree) popVars(n int) { - t.vars = t.vars[:n] -} - -// useVar returns a node for a variable reference. It errors if the -// variable is not defined. -func (t *Tree) useVar(name string) Node { - v := newVariable(name) - for _, varName := range t.vars { - if varName == v.Ident[0] { - return v - } - } - t.errorf("undefined variable %q", v.Ident[0]) - return nil -} diff --git a/src/pkg/exp/template/parse/parse_test.go b/src/pkg/exp/template/parse/parse_test.go deleted file mode 100644 index 1928c319d..000000000 --- a/src/pkg/exp/template/parse/parse_test.go +++ /dev/null @@ -1,259 +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 parse - -import ( - "flag" - "fmt" - "testing" -) - -var debug = flag.Bool("debug", false, "show the errors produced by the tests") - -type numberTest struct { - text string - isInt bool - isUint bool - isFloat bool - isComplex bool - int64 - uint64 - float64 - complex128 -} - -var numberTests = []numberTest{ - // basics - {"0", true, true, true, false, 0, 0, 0, 0}, - {"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint. - {"73", true, true, true, false, 73, 73, 73, 0}, - {"073", true, true, true, false, 073, 073, 073, 0}, - {"0x73", true, true, true, false, 0x73, 0x73, 0x73, 0}, - {"-73", true, false, true, false, -73, 0, -73, 0}, - {"+73", true, false, true, false, 73, 0, 73, 0}, - {"100", true, true, true, false, 100, 100, 100, 0}, - {"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0}, - {"-1e9", true, false, true, false, -1e9, 0, -1e9, 0}, - {"-1.2", false, false, true, false, 0, 0, -1.2, 0}, - {"1e19", false, true, true, false, 0, 1e19, 1e19, 0}, - {"-1e19", false, false, true, false, 0, 0, -1e19, 0}, - {"4i", false, false, false, true, 0, 0, 0, 4i}, - {"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i}, - {"073i", false, false, false, true, 0, 0, 0, 73i}, // not octal! - // complex with 0 imaginary are float (and maybe integer) - {"0i", true, true, true, true, 0, 0, 0, 0}, - {"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2}, - {"-12+0i", true, false, true, true, -12, 0, -12, -12}, - {"13+0i", true, true, true, true, 13, 13, 13, 13}, - // funny bases - {"0123", true, true, true, false, 0123, 0123, 0123, 0}, - {"-0x0", true, true, true, false, 0, 0, 0, 0}, - {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0}, - // character constants - {`'a'`, true, true, true, false, 'a', 'a', 'a', 0}, - {`'\n'`, true, true, true, false, '\n', '\n', '\n', 0}, - {`'\\'`, true, true, true, false, '\\', '\\', '\\', 0}, - {`'\''`, true, true, true, false, '\'', '\'', '\'', 0}, - {`'\xFF'`, true, true, true, false, 0xFF, 0xFF, 0xFF, 0}, - {`'パ'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0}, - {`'\u30d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0}, - {`'\U000030d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0}, - // some broken syntax - {text: "+-2"}, - {text: "0x123."}, - {text: "1e."}, - {text: "0xi."}, - {text: "1+2."}, - {text: "'x"}, - {text: "'xx'"}, -} - -func TestNumberParse(t *testing.T) { - for _, test := range numberTests { - // If fmt.Sscan thinks it's complex, it's complex. We can't trust the output - // because imaginary comes out as a number. - var c complex128 - typ := itemNumber - if test.text[0] == '\'' { - typ = itemCharConstant - } else { - _, err := fmt.Sscan(test.text, &c) - if err == nil { - typ = itemComplex - } - } - n, err := newNumber(test.text, typ) - ok := test.isInt || test.isUint || test.isFloat || test.isComplex - if ok && err != nil { - t.Errorf("unexpected error for %q: %s", test.text, err) - continue - } - if !ok && err == nil { - t.Errorf("expected error for %q", test.text) - continue - } - if !ok { - if *debug { - fmt.Printf("%s\n\t%s\n", test.text, err) - } - continue - } - if n.IsComplex != test.isComplex { - t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex) - } - if test.isInt { - if !n.IsInt { - t.Errorf("expected integer for %q", test.text) - } - if n.Int64 != test.int64 { - t.Errorf("int64 for %q should be %d Is %d", test.text, test.int64, n.Int64) - } - } else if n.IsInt { - t.Errorf("did not expect integer for %q", test.text) - } - if test.isUint { - if !n.IsUint { - t.Errorf("expected unsigned integer for %q", test.text) - } - if n.Uint64 != test.uint64 { - t.Errorf("uint64 for %q should be %d Is %d", test.text, test.uint64, n.Uint64) - } - } else if n.IsUint { - t.Errorf("did not expect unsigned integer for %q", test.text) - } - if test.isFloat { - if !n.IsFloat { - t.Errorf("expected float for %q", test.text) - } - if n.Float64 != test.float64 { - t.Errorf("float64 for %q should be %g Is %g", test.text, test.float64, n.Float64) - } - } else if n.IsFloat { - t.Errorf("did not expect float for %q", test.text) - } - if test.isComplex { - if !n.IsComplex { - t.Errorf("expected complex for %q", test.text) - } - if n.Complex128 != test.complex128 { - t.Errorf("complex128 for %q should be %g Is %g", test.text, test.complex128, n.Complex128) - } - } else if n.IsComplex { - t.Errorf("did not expect complex for %q", test.text) - } - } -} - -type parseTest struct { - name string - input string - ok bool - result string -} - -const ( - noError = true - hasError = false -) - -var parseTests = []parseTest{ - {"empty", "", noError, - `[]`}, - {"comment", "{{/*\n\n\n*/}}", noError, - `[]`}, - {"spaces", " \t\n", noError, - `[(text: " \t\n")]`}, - {"text", "some text", noError, - `[(text: "some text")]`}, - {"emptyAction", "{{}}", hasError, - `[(action: [])]`}, - {"field", "{{.X}}", noError, - `[(action: [(command: [F=[X]])])]`}, - {"simple command", "{{printf}}", noError, - `[(action: [(command: [I=printf])])]`}, - {"$ invocation", "{{$}}", noError, - "[(action: [(command: [V=[$]])])]"}, - {"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError, - "[({{with [V=[$x]] := [(command: [N=3])]}} [(action: [(command: [V=[$x] N=23])])])]"}, - {"variable with fields", "{{$.I}}", noError, - "[(action: [(command: [V=[$ I]])])]"}, - {"multi-word command", "{{printf `%d` 23}}", noError, - "[(action: [(command: [I=printf S=`%d` N=23])])]"}, - {"pipeline", "{{.X|.Y}}", noError, - `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`}, - {"pipeline with decl", "{{$x := .X|.Y}}", noError, - `[(action: [V=[$x]] := [(command: [F=[X]]) (command: [F=[Y]])])]`}, - {"declaration", "{{.X|.Y}}", noError, - `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`}, - {"simple if", "{{if .X}}hello{{end}}", noError, - `[({{if [(command: [F=[X]])]}} [(text: "hello")])]`}, - {"if with else", "{{if .X}}true{{else}}false{{end}}", noError, - `[({{if [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, - {"simple range", "{{range .X}}hello{{end}}", noError, - `[({{range [(command: [F=[X]])]}} [(text: "hello")])]`}, - {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError, - `[({{range [(command: [F=[X Y Z]])]}} [(text: "hello")])]`}, - {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError, - `[({{range [(command: [F=[X]])]}} [(text: "hello")({{range [(command: [F=[Y]])]}} [(text: "goodbye")])])]`}, - {"range with else", "{{range .X}}true{{else}}false{{end}}", noError, - `[({{range [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, - {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError, - `[({{range [(command: [F=[X]]) (command: [F=[M]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, - {"range []int", "{{range .SI}}{{.}}{{end}}", noError, - `[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`}, - {"constants", "{{range .SI 1 -3.2i true false 'a'}}{{end}}", noError, - `[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false N='a'])]}} [])]`}, - {"template", "{{template `x`}}", noError, - `[{{template "x"}}]`}, - {"template with arg", "{{template `x` .Y}}", noError, - `[{{template "x" [(command: [F=[Y]])]}}]`}, - {"with", "{{with .X}}hello{{end}}", noError, - `[({{with [(command: [F=[X]])]}} [(text: "hello")])]`}, - {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError, - `[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`}, - // Errors. - {"unclosed action", "hello{{range", hasError, ""}, - {"unmatched end", "{{end}}", hasError, ""}, - {"missing end", "hello{{range .x}}", hasError, ""}, - {"missing end after else", "hello{{range .x}}{{else}}", hasError, ""}, - {"undefined function", "hello{{undefined}}", hasError, ""}, - {"undefined variable", "{{$x}}", hasError, ""}, - {"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""}, - {"variable undefined in template", "{{template $v}}", hasError, ""}, - {"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""}, - {"template with field ref", "{{template .X}}", hasError, ""}, - {"template with var", "{{template $v}}", hasError, ""}, - {"invalid punctuation", "{{printf 3, 4}}", hasError, ""}, - {"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""}, - {"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""}, -} - -var builtins = map[string]interface{}{ - "printf": fmt.Sprintf, -} - -func TestParse(t *testing.T) { - for _, test := range parseTests { - tmpl, err := New(test.name).Parse(test.input, builtins) - switch { - case err == nil && !test.ok: - t.Errorf("%q: expected error; got none", test.name) - continue - case err != nil && test.ok: - t.Errorf("%q: unexpected error: %v", test.name, err) - continue - case err != nil && !test.ok: - // expected error, got one - if *debug { - fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) - } - continue - } - result := tmpl.Root.String() - if result != test.result { - t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result) - } - } -} diff --git a/src/pkg/exp/template/parse/set.go b/src/pkg/exp/template/parse/set.go deleted file mode 100644 index 4820da925..000000000 --- a/src/pkg/exp/template/parse/set.go +++ /dev/null @@ -1,50 +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 parse - -import ( - "fmt" - "os" - "strconv" -) - -// Set returns a slice of Trees created by parsing the template set -// definition in the argument string. If an error is encountered, -// parsing stops and an empty slice is returned with the error. -func Set(text string, funcs ...map[string]interface{}) (tree map[string]*Tree, err os.Error) { - tree = make(map[string]*Tree) - defer (*Tree)(nil).recover(&err) - lex := lex("set", text) - const context = "define clause" - for { - t := New("set") // name will be updated once we know it. - t.startParse(funcs, lex) - // Expect EOF or "{{ define name }}". - if t.atEOF() { - break - } - t.expect(itemLeftDelim, context) - t.expect(itemDefine, context) - name := t.expect(itemString, context) - t.Name, err = strconv.Unquote(name.val) - if err != nil { - t.error(err) - } - t.expect(itemRightDelim, context) - end := t.parse(false) - if end == nil { - t.errorf("unexpected EOF in %s", context) - } - if end.Type() != NodeEnd { - t.errorf("unexpected %s in %s", end, context) - } - t.stopParse() - if _, present := tree[t.Name]; present { - return nil, fmt.Errorf("template: %q multiply defined", name) - } - tree[t.Name] = t - } - return -} diff --git a/src/pkg/exp/template/set.go b/src/pkg/exp/template/set.go deleted file mode 100644 index 7f2813c04..000000000 --- a/src/pkg/exp/template/set.go +++ /dev/null @@ -1,108 +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 template - -import ( - "exp/template/parse" - "fmt" - "io" - "os" - "reflect" -) - -// Set holds a set of related templates that can refer to one another by name. -// The zero value represents an empty set. -// A template may be a member of multiple sets. -type Set struct { - tmpl map[string]*Template - parseFuncs FuncMap - execFuncs map[string]reflect.Value -} - -func (s *Set) init() { - if s.tmpl == nil { - s.tmpl = make(map[string]*Template) - s.parseFuncs = make(FuncMap) - s.execFuncs = make(map[string]reflect.Value) - } -} - -// Funcs adds the elements of the argument map to the set's function map. It -// panics if a value in the map is not a function with appropriate return -// type. -// The return value is the set, so calls can be chained. -func (s *Set) Funcs(funcMap FuncMap) *Set { - s.init() - addValueFuncs(s.execFuncs, funcMap) - addFuncs(s.parseFuncs, funcMap) - return s -} - -// Add adds the argument templates to the set. It panics if two templates -// with the same name are added or if a template is already a member of -// a set. -// The return value is the set, so calls can be chained. -func (s *Set) Add(templates ...*Template) *Set { - for _, t := range templates { - if err := s.add(t); err != nil { - panic(err) - } - } - return s -} - -// add adds the argument template to the set. -func (s *Set) add(t *Template) os.Error { - s.init() - if t.set != nil { - return fmt.Errorf("template: %q already in a set", t.name) - } - if _, ok := s.tmpl[t.name]; ok { - return fmt.Errorf("template: %q already defined in set", t.name) - } - s.tmpl[t.name] = t - t.set = s - return nil -} - -// Template returns the template with the given name in the set, -// or nil if there is no such template. -func (s *Set) Template(name string) *Template { - return s.tmpl[name] -} - -// FuncMap returns the set's function map. -func (s *Set) FuncMap() FuncMap { - return s.parseFuncs -} - -// Execute applies the named template to the specified data object, writing -// the output to wr. -func (s *Set) Execute(wr io.Writer, name string, data interface{}) os.Error { - tmpl := s.tmpl[name] - if tmpl == nil { - return fmt.Errorf("template: no template %q in set", name) - } - return tmpl.Execute(wr, data) -} - -// Parse parses a string into a set of named templates. Parse may be called -// multiple times for a given set, adding the templates defined in the string -// to the set. If a template is redefined, the element in the set is -// overwritten with the new definition. -func (s *Set) Parse(text string) (*Set, os.Error) { - trees, err := parse.Set(text, s.parseFuncs, builtins) - if err != nil { - return nil, err - } - s.init() - for name, tree := range trees { - tmpl := New(name) - tmpl.Tree = tree - tmpl.addToSet(s) - s.tmpl[name] = tmpl - } - return s, nil -} diff --git a/src/pkg/exp/template/set_test.go b/src/pkg/exp/template/set_test.go deleted file mode 100644 index 6fa29063b..000000000 --- a/src/pkg/exp/template/set_test.go +++ /dev/null @@ -1,239 +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 template - -import ( - "fmt" - "testing" -) - -const ( - noError = true - hasError = false -) - -type setParseTest struct { - name string - input string - ok bool - names []string - results []string -} - -var setParseTests = []setParseTest{ - {"empty", "", noError, - nil, - nil}, - {"one", `{{define "foo"}} FOO {{end}}`, noError, - []string{"foo"}, - []string{`[(text: " FOO ")]`}}, - {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, - []string{"foo", "bar"}, - []string{`[(text: " FOO ")]`, `[(text: " BAR ")]`}}, - // errors - {"missing end", `{{define "foo"}} FOO `, hasError, - nil, - nil}, - {"malformed name", `{{define "foo}} FOO `, hasError, - nil, - nil}, -} - -func TestSetParse(t *testing.T) { - for _, test := range setParseTests { - set, err := new(Set).Parse(test.input) - switch { - case err == nil && !test.ok: - t.Errorf("%q: expected error; got none", test.name) - continue - case err != nil && test.ok: - t.Errorf("%q: unexpected error: %v", test.name, err) - continue - case err != nil && !test.ok: - // expected error, got one - if *debug { - fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) - } - continue - } - if set == nil { - continue - } - if len(set.tmpl) != len(test.names) { - t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(set.tmpl)) - continue - } - for i, name := range test.names { - tmpl, ok := set.tmpl[name] - if !ok { - t.Errorf("%s: can't find template %q", test.name, name) - continue - } - result := tmpl.Root.String() - if result != test.results[i] { - t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) - } - } - } -} - -var setExecTests = []execTest{ - {"empty", "", "", nil, true}, - {"text", "some text", "some text", nil, true}, - {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true}, - {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true}, - {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, - {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, - {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, - {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, - {"variable declared by template", `{{template "nested" $x=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true}, - - // User-defined function: test argument evaluator. - {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true}, - {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true}, -} - -// These strings are also in testdata/*. -const setText1 = ` - {{define "x"}}TEXT{{end}} - {{define "dotV"}}{{.V}}{{end}} -` - -const setText2 = ` - {{define "dot"}}{{.}}{{end}} - {{define "nested"}}{{template "dot" .}}{{end}} -` - -func TestSetExecute(t *testing.T) { - // Declare a set with a couple of templates first. - set := new(Set) - _, err := set.Parse(setText1) - if err != nil { - t.Fatalf("error parsing set: %s", err) - } - _, err = set.Parse(setText2) - if err != nil { - t.Fatalf("error parsing set: %s", err) - } - testExecute(setExecTests, set, t) -} - -func TestSetParseFile(t *testing.T) { - set := new(Set) - _, err := set.ParseFile("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = set.ParseFile("testdata/file1.tmpl", "testdata/file2.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(setExecTests, set, t) -} - -func TestParseSetFile(t *testing.T) { - set := new(Set) - _, err := ParseSetFile("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - set, err = ParseSetFile("testdata/file1.tmpl", "testdata/file2.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(setExecTests, set, t) -} - -func TestSetParseFiles(t *testing.T) { - _, err := new(Set).ParseFiles("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = new(Set).ParseFiles("[x") - if err == nil { - t.Error("expected error for bad pattern; got none") - } - set, err := new(Set).ParseFiles("testdata/file*.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(setExecTests, set, t) -} - -func TestParseSetFiles(t *testing.T) { - _, err := ParseSetFiles("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = ParseSetFiles("[x") - if err == nil { - t.Error("expected error for bad pattern; got none") - } - set, err := ParseSetFiles("testdata/file*.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(setExecTests, set, t) -} - -var templateFileExecTests = []execTest{ - {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\ntemplate2\n", 0, true}, -} - -func TestSetParseTemplateFile(t *testing.T) { - _, err := ParseTemplateFile("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - set, err := new(Set).ParseTemplateFile("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(templateFileExecTests, set, t) -} - -func TestParseTemplateFile(t *testing.T) { - _, err := ParseTemplateFile("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - set, err := new(Set).ParseTemplateFile("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(templateFileExecTests, set, t) -} - -func TestSetParseTemplateFiles(t *testing.T) { - _, err := ParseTemplateFiles("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = new(Set).ParseTemplateFiles("[x") - if err == nil { - t.Error("expected error for bad pattern; got none") - } - set, err := new(Set).ParseTemplateFiles("testdata/tmpl*.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(templateFileExecTests, set, t) -} - -func TestParseTemplateFiles(t *testing.T) { - _, err := ParseTemplateFiles("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = ParseTemplateFiles("[x") - if err == nil { - t.Error("expected error for bad pattern; got none") - } - set, err := ParseTemplateFiles("testdata/tmpl*.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(templateFileExecTests, set, t) -} diff --git a/src/pkg/exp/template/testdata/file1.tmpl b/src/pkg/exp/template/testdata/file1.tmpl deleted file mode 100644 index febf9d9f8..000000000 --- a/src/pkg/exp/template/testdata/file1.tmpl +++ /dev/null @@ -1,2 +0,0 @@ -{{define "x"}}TEXT{{end}} -{{define "dotV"}}{{.V}}{{end}} diff --git a/src/pkg/exp/template/testdata/file2.tmpl b/src/pkg/exp/template/testdata/file2.tmpl deleted file mode 100644 index 39bf6fb9e..000000000 --- a/src/pkg/exp/template/testdata/file2.tmpl +++ /dev/null @@ -1,2 +0,0 @@ -{{define "dot"}}{{.}}{{end}} -{{define "nested"}}{{template "dot" .}}{{end}} diff --git a/src/pkg/exp/template/testdata/tmpl1.tmpl b/src/pkg/exp/template/testdata/tmpl1.tmpl deleted file mode 100644 index 3d15b8173..000000000 --- a/src/pkg/exp/template/testdata/tmpl1.tmpl +++ /dev/null @@ -1 +0,0 @@ -template1 diff --git a/src/pkg/exp/template/testdata/tmpl2.tmpl b/src/pkg/exp/template/testdata/tmpl2.tmpl deleted file mode 100644 index a374d2fe7..000000000 --- a/src/pkg/exp/template/testdata/tmpl2.tmpl +++ /dev/null @@ -1 +0,0 @@ -template2 diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go index 2a24b1455..e1989226b 100644 --- a/src/pkg/go/doc/comment.go +++ b/src/pkg/go/doc/comment.go @@ -11,7 +11,7 @@ import ( "io" "regexp" "strings" - "template" // for htmlEscape + "template" // for HTMLEscape ) func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' } diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go index 618a15076..9c14d1667 100644 --- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -420,10 +420,10 @@ func (p *parser) parseExprList(lhs bool) (list []ast.Expr) { defer un(trace(p, "ExpressionList")) } - list = append(list, p.parseExpr(lhs)) + list = append(list, p.checkExpr(p.parseExpr(lhs))) for p.tok == token.COMMA { p.next() - list = append(list, p.parseExpr(lhs)) + list = append(list, p.checkExpr(p.parseExpr(lhs))) } return @@ -973,7 +973,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr { lparen := p.pos p.next() p.exprLev++ - x := p.parseRhs() + x := p.parseRhsOrType() // types may be parenthesized: (some type) p.exprLev-- rparen := p.expect(token.RPAREN) return &ast.ParenExpr{lparen, x, rparen} @@ -1062,7 +1062,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { var list []ast.Expr var ellipsis token.Pos for p.tok != token.RPAREN && p.tok != token.EOF && !ellipsis.IsValid() { - list = append(list, p.parseRhs()) + list = append(list, p.parseRhsOrType()) // builtins may expect a type: make(some type, ...) if p.tok == token.ELLIPSIS { ellipsis = p.pos p.next() @@ -1087,7 +1087,7 @@ func (p *parser) parseElement(keyOk bool) ast.Expr { return p.parseLiteralValue(nil) } - x := p.parseExpr(keyOk) // don't resolve if map key + x := p.checkExpr(p.parseExpr(keyOk)) // don't resolve if map key if keyOk { if p.tok == token.COLON { colon := p.pos @@ -1146,19 +1146,14 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr { case *ast.IndexExpr: case *ast.SliceExpr: case *ast.TypeAssertExpr: - if t.Type == nil { - // the form X.(type) is only allowed in type switch expressions - p.errorExpected(x.Pos(), "expression") - x = &ast.BadExpr{x.Pos(), x.End()} - } + // If t.Type == nil we have a type assertion of the form + // y.(type), which is only allowed in type switch expressions. + // It's hard to exclude those but for the case where we are in + // a type switch. Instead be lenient and test this in the type + // checker. case *ast.CallExpr: case *ast.StarExpr: case *ast.UnaryExpr: - if t.Op == token.RANGE { - // the range operator is only allowed at the top of a for statement - p.errorExpected(x.Pos(), "expression") - x = &ast.BadExpr{x.Pos(), x.End()} - } case *ast.BinaryExpr: default: // all other nodes are not proper expressions @@ -1223,11 +1218,6 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr { case *ast.ParenExpr: panic("unreachable") case *ast.UnaryExpr: - if t.Op == token.RANGE { - // the range operator is only allowed at the top of a for statement - p.errorExpected(x.Pos(), "expression") - x = &ast.BadExpr{x.Pos(), x.End()} - } case *ast.ArrayType: if len, isEllipsis := t.Len.(*ast.Ellipsis); isEllipsis { p.error(len.Pos(), "expected array length, found '...'") @@ -1300,7 +1290,7 @@ func (p *parser) parseUnaryExpr(lhs bool) ast.Expr { } switch p.tok { - case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.RANGE: + case token.ADD, token.SUB, token.NOT, token.XOR, token.AND: pos, op := p.pos, p.tok p.next() x := p.parseUnaryExpr(false) @@ -1354,8 +1344,9 @@ func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr { } // If lhs is set and the result is an identifier, it is not resolved. -// TODO(gri): parseExpr may return a type or even a raw type ([..]int) - -// should reject when a type/raw type is obviously not allowed +// The result may be a type or even a raw type ([...]int). Callers must +// check the result (using checkExpr or checkExprOrType), depending on +// context. func (p *parser) parseExpr(lhs bool) ast.Expr { if p.trace { defer un(trace(p, "Expression")) @@ -1365,13 +1356,28 @@ func (p *parser) parseExpr(lhs bool) ast.Expr { } func (p *parser) parseRhs() ast.Expr { - return p.parseExpr(false) + return p.checkExpr(p.parseExpr(false)) +} + +func (p *parser) parseRhsOrType() ast.Expr { + return p.checkExprOrType(p.parseExpr(false)) } // ---------------------------------------------------------------------------- // Statements -func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt { +// Parsing modes for parseSimpleStmt. +const ( + basic = iota + labelOk + rangeOk +) + +// parseSimpleStmt returns true as 2nd result if it parsed the assignment +// of a range clause (with mode == rangeOk). The returned statement is an +// assignment with a right-hand side that is a single unary expression of +// the form "range x". No guarantees are given for the left-hand side. +func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) { if p.trace { defer un(trace(p, "SimpleStmt")) } @@ -1384,11 +1390,20 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt { token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN, token.REM_ASSIGN, token.AND_ASSIGN, token.OR_ASSIGN, token.XOR_ASSIGN, token.SHL_ASSIGN, token.SHR_ASSIGN, token.AND_NOT_ASSIGN: - // assignment statement + // assignment statement, possibly part of a range clause pos, tok := p.pos, p.tok p.next() - y := p.parseRhsList() - return &ast.AssignStmt{x, pos, tok, y} + var y []ast.Expr + isRange := false + if mode == rangeOk && p.tok == token.RANGE && (tok == token.DEFINE || tok == token.ASSIGN) { + pos := p.pos + p.next() + y = []ast.Expr{&ast.UnaryExpr{pos, token.RANGE, p.parseRhs()}} + isRange = true + } else { + y = p.parseRhsList() + } + return &ast.AssignStmt{x, pos, tok, y}, isRange } if len(x) > 1 { @@ -1401,13 +1416,13 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt { // labeled statement colon := p.pos p.next() - if label, isIdent := x[0].(*ast.Ident); labelOk && isIdent { + if label, isIdent := x[0].(*ast.Ident); mode == labelOk && isIdent { // Go spec: The scope of a label is the body of the function // in which it is declared and excludes the body of any nested // function. stmt := &ast.LabeledStmt{label, colon, p.parseStmt()} p.declare(stmt, nil, p.labelScope, ast.Lbl, label) - return stmt + return stmt, false } // The label declaration typically starts at x[0].Pos(), but the label // declaration may be erroneous due to a token after that position (and @@ -1416,28 +1431,28 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt { // before the ':' that caused the problem. Thus, use the (latest) colon // position for error reporting. p.error(colon, "illegal label declaration") - return &ast.BadStmt{x[0].Pos(), colon + 1} + return &ast.BadStmt{x[0].Pos(), colon + 1}, false case token.ARROW: // send statement arrow := p.pos p.next() // consume "<-" y := p.parseRhs() - return &ast.SendStmt{x[0], arrow, y} + return &ast.SendStmt{x[0], arrow, y}, false case token.INC, token.DEC: // increment or decrement s := &ast.IncDecStmt{x[0], p.pos, p.tok} p.next() // consume "++" or "--" - return s + return s, false } // expression - return &ast.ExprStmt{x[0]} + return &ast.ExprStmt{x[0]}, false } func (p *parser) parseCallExpr() *ast.CallExpr { - x := p.parseRhs() + x := p.parseRhsOrType() // could be a conversion: (some type)(x) if call, isCall := x.(*ast.CallExpr); isCall { return call } @@ -1538,7 +1553,7 @@ func (p *parser) parseIfStmt() *ast.IfStmt { p.next() x = p.parseRhs() } else { - s = p.parseSimpleStmt(false) + s, _ = p.parseSimpleStmt(basic) if p.tok == token.SEMICOLON { p.next() x = p.parseRhs() @@ -1629,14 +1644,14 @@ func (p *parser) parseSwitchStmt() ast.Stmt { prevLev := p.exprLev p.exprLev = -1 if p.tok != token.SEMICOLON { - s2 = p.parseSimpleStmt(false) + s2, _ = p.parseSimpleStmt(basic) } if p.tok == token.SEMICOLON { p.next() s1 = s2 s2 = nil if p.tok != token.LBRACE { - s2 = p.parseSimpleStmt(false) + s2, _ = p.parseSimpleStmt(basic) } } p.exprLev = prevLev @@ -1749,22 +1764,23 @@ func (p *parser) parseForStmt() ast.Stmt { defer p.closeScope() var s1, s2, s3 ast.Stmt + var isRange bool if p.tok != token.LBRACE { prevLev := p.exprLev p.exprLev = -1 if p.tok != token.SEMICOLON { - s2 = p.parseSimpleStmt(false) + s2, isRange = p.parseSimpleStmt(rangeOk) } - if p.tok == token.SEMICOLON { + if !isRange && p.tok == token.SEMICOLON { p.next() s1 = s2 s2 = nil if p.tok != token.SEMICOLON { - s2 = p.parseSimpleStmt(false) + s2, _ = p.parseSimpleStmt(basic) } p.expectSemi() if p.tok != token.LBRACE { - s3 = p.parseSimpleStmt(false) + s3, _ = p.parseSimpleStmt(basic) } } p.exprLev = prevLev @@ -1773,12 +1789,8 @@ func (p *parser) parseForStmt() ast.Stmt { body := p.parseBlockStmt() p.expectSemi() - if as, isAssign := s2.(*ast.AssignStmt); isAssign { - // possibly a for statement with a range clause; check assignment operator - if as.Tok != token.ASSIGN && as.Tok != token.DEFINE { - p.errorExpected(as.TokPos, "'=' or ':='") - return &ast.BadStmt{pos, body.End()} - } + if isRange { + as := s2.(*ast.AssignStmt) // check lhs var key, value ast.Expr switch len(as.Lhs) { @@ -1790,18 +1802,10 @@ func (p *parser) parseForStmt() ast.Stmt { p.errorExpected(as.Lhs[0].Pos(), "1 or 2 expressions") return &ast.BadStmt{pos, body.End()} } - // check rhs - if len(as.Rhs) != 1 { - p.errorExpected(as.Rhs[0].Pos(), "1 expression") - return &ast.BadStmt{pos, body.End()} - } - if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE { - // rhs is range expression - // (any short variable declaration was handled by parseSimpleStat above) - return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body} - } - p.errorExpected(s2.Pos(), "range clause") - return &ast.BadStmt{pos, body.End()} + // parseSimpleStmt returned a right-hand side that + // is a single unary expression of the form "range x" + x := as.Rhs[0].(*ast.UnaryExpr).X + return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, x, body} } // regular for statement @@ -1821,7 +1825,7 @@ func (p *parser) parseStmt() (s ast.Stmt) { token.IDENT, token.INT, token.FLOAT, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operand token.LBRACK, token.STRUCT, // composite type token.MUL, token.AND, token.ARROW, token.ADD, token.SUB, token.XOR: // unary operators - s = p.parseSimpleStmt(true) + s, _ = p.parseSimpleStmt(labelOk) // because of the required look-ahead, labeled statements are // parsed by parseSimpleStmt - don't expect a semicolon after // them @@ -2153,6 +2157,5 @@ func (p *parser) parseFile() *ast.File { } } - // TODO(gri): store p.imports in AST return &ast.File{doc, pos, ident, decls, p.pkgScope, p.imports, p.unresolved[0:i], p.comments} } diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go index 8269db839..39a78e515 100644 --- a/src/pkg/go/parser/parser_test.go +++ b/src/pkg/go/parser/parser_test.go @@ -21,6 +21,24 @@ var illegalInputs = []interface{}{ `package p; func f() { if ; /* should have condition */ {} };`, `package p; func f() { if f(); /* should have condition */ {} };`, `package p; const c; /* should have constant value */`, + `package p; func f() { if _ = range x; true {} };`, + `package p; func f() { switch _ = range x; true {} };`, + `package p; func f() { for _ = range x ; ; {} };`, + `package p; func f() { for ; ; _ = range x {} };`, + `package p; func f() { for ; _ = range x ; {} };`, + `package p; var a = [1]int; /* illegal expression */`, + `package p; var a = [...]int; /* illegal expression */`, + `package p; var a = struct{} /* illegal expression */`, + `package p; var a = func(); /* illegal expression */`, + `package p; var a = interface{} /* illegal expression */`, + `package p; var a = []int /* illegal expression */`, + `package p; var a = map[int]int /* illegal expression */`, + `package p; var a = chan int; /* illegal expression */`, + `package p; var a = []int{[]int}; /* illegal expression */`, + `package p; var a = ([]int); /* illegal expression */`, + `package p; var a = a[[]int:[]int]; /* illegal expression */`, + `package p; var a = <- chan int; /* illegal expression */`, + `package p; func f() { select { case _ <- chan int: } };`, } func TestParseIllegalInputs(t *testing.T) { @@ -51,6 +69,7 @@ var validPrograms = []interface{}{ `package p; func f() { select { case x := (<-c): } };`, `package p; func f() { if ; true {} };`, `package p; func f() { switch ; {} };`, + `package p; func f() { for _ = range "foo" + "bar" {} };`, } func TestParseValidPrograms(t *testing.T) { diff --git a/src/pkg/html/token.go b/src/pkg/html/token.go index fddc922d6..d266b3a30 100644 --- a/src/pkg/html/token.go +++ b/src/pkg/html/token.go @@ -429,7 +429,7 @@ loop: for ; i < z.p1; i++ { c := z.buf[i] switch c { - case '<', '>', '"', '\'', '/', '=': + case '>', '/', '=': break loop } switch { diff --git a/src/pkg/html/token_test.go b/src/pkg/html/token_test.go index 1330f3247..0a0beb201 100644 --- a/src/pkg/html/token_test.go +++ b/src/pkg/html/token_test.go @@ -49,14 +49,24 @@ var tokenTests = []tokenTest{ }, { "malformed tag #1", + `

`, + `

`, + }, + { + "malformed tag #2", `

`, `

`, }, { - "malformed tag #2", + "malformed tag #3", `

`, }, + { + "malformed tag #4", + `

`, + `

`, + }, // Comments. { "comment0", diff --git a/src/pkg/http/Makefile b/src/pkg/http/Makefile index b8bc093d4..df4ab9510 100644 --- a/src/pkg/http/Makefile +++ b/src/pkg/http/Makefile @@ -22,6 +22,5 @@ GOFILES=\ status.go\ transfer.go\ transport.go\ - url.go\ include ../../Make.pkg diff --git a/src/pkg/http/cgi/child.go b/src/pkg/http/cgi/child.go index 8b74d7054..8d0eca8d5 100644 --- a/src/pkg/http/cgi/child.go +++ b/src/pkg/http/cgi/child.go @@ -18,6 +18,7 @@ import ( "os" "strconv" "strings" + "url" ) // Request returns the HTTP request as represented in the current @@ -93,7 +94,7 @@ func RequestFromMap(params map[string]string) (*http.Request, os.Error) { // Hostname is provided, so we can reasonably construct a URL, // even if we have to assume 'http' for the scheme. r.RawURL = "http://" + r.Host + params["REQUEST_URI"] - url, err := http.ParseURL(r.RawURL) + url, err := url.Parse(r.RawURL) if err != nil { return nil, os.NewError("cgi: failed to parse host and REQUEST_URI into a URL: " + r.RawURL) } @@ -103,7 +104,7 @@ func RequestFromMap(params map[string]string) (*http.Request, os.Error) { // failed to parse if r.URL == nil { r.RawURL = params["REQUEST_URI"] - url, err := http.ParseURL(r.RawURL) + url, err := url.Parse(r.RawURL) if err != nil { return nil, os.NewError("cgi: failed to parse REQUEST_URI into a URL: " + r.RawURL) } diff --git a/src/pkg/http/cgi/host.go b/src/pkg/http/cgi/host.go index 93825b391..f7de89f99 100644 --- a/src/pkg/http/cgi/host.go +++ b/src/pkg/http/cgi/host.go @@ -276,7 +276,7 @@ func (h *Handler) printf(format string, v ...interface{}) { } func (h *Handler) handleInternalRedirect(rw http.ResponseWriter, req *http.Request, path string) { - url, err := req.URL.ParseURL(path) + url, err := req.URL.Parse(path) if err != nil { rw.WriteHeader(http.StatusInternalServerError) h.printf("cgi: error resolving local URI path %q: %v", path, err) diff --git a/src/pkg/http/client.go b/src/pkg/http/client.go index 6ea7dee03..44b3443fc 100644 --- a/src/pkg/http/client.go +++ b/src/pkg/http/client.go @@ -12,6 +12,7 @@ import ( "io" "os" "strings" + "url" ) // A Client is an HTTP client. Its zero value (DefaultClient) is a usable client @@ -158,7 +159,7 @@ func (c *Client) Get(url string) (r *Response, err os.Error) { func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) { // TODO: if/when we add cookie support, the redirected request shouldn't // necessarily supply the same cookies as the original. - var base *URL + var base *url.URL redirectChecker := c.CheckRedirect if redirectChecker == nil { redirectChecker = defaultCheckRedirect @@ -166,13 +167,13 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) var via []*Request req := ireq - url := "" // next relative or absolute URL to fetch (after first request) + urlStr := "" // next relative or absolute URL to fetch (after first request) for redirect := 0; ; redirect++ { if redirect != 0 { req = new(Request) req.Method = ireq.Method req.Header = make(Header) - req.URL, err = base.ParseURL(url) + req.URL, err = base.Parse(urlStr) if err != nil { break } @@ -190,13 +191,13 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) } } - url = req.URL.String() + urlStr = req.URL.String() if r, err = send(req, c.Transport); err != nil { break } if shouldRedirect(r.StatusCode) { r.Body.Close() - if url = r.Header.Get("Location"); url == "" { + if urlStr = r.Header.Get("Location"); urlStr == "" { err = os.NewError(fmt.Sprintf("%d response missing Location header", r.StatusCode)) break } @@ -208,7 +209,7 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) } method := ireq.Method - err = &URLError{method[0:1] + strings.ToLower(method[1:]), url, err} + err = &url.Error{method[0:1] + strings.ToLower(method[1:]), urlStr, err} return } @@ -246,7 +247,7 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, // Caller should close r.Body when done reading from it. // // PostForm is a wrapper around DefaultClient.PostForm -func PostForm(url string, data Values) (r *Response, err os.Error) { +func PostForm(url string, data url.Values) (r *Response, err os.Error) { return DefaultClient.PostForm(url, data) } @@ -254,7 +255,7 @@ func PostForm(url string, data Values) (r *Response, err os.Error) { // with data's keys and values urlencoded as the request body. // // Caller should close r.Body when done reading from it. -func (c *Client) PostForm(url string, data Values) (r *Response, err os.Error) { +func (c *Client) PostForm(url string, data url.Values) (r *Response, err os.Error) { return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) } diff --git a/src/pkg/http/client_test.go b/src/pkg/http/client_test.go index 3b8558535..f22cce50b 100644 --- a/src/pkg/http/client_test.go +++ b/src/pkg/http/client_test.go @@ -17,6 +17,7 @@ import ( "strconv" "strings" "testing" + "url" ) var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) { @@ -109,18 +110,18 @@ func TestPostFormRequestFormat(t *testing.T) { tr := &recordingTransport{} client := &Client{Transport: tr} - url := "http://dummy.faketld/" - form := make(Values) + urlStr := "http://dummy.faketld/" + form := make(url.Values) form.Set("foo", "bar") form.Add("foo", "bar2") form.Set("bar", "baz") - client.PostForm(url, form) // Note: doesn't hit network + client.PostForm(urlStr, form) // Note: doesn't hit network if tr.req.Method != "POST" { t.Errorf("got method %q, want %q", tr.req.Method, "POST") } - if tr.req.URL.String() != url { - t.Errorf("got URL %q, want %q", tr.req.URL.String(), url) + if tr.req.URL.String() != urlStr { + t.Errorf("got URL %q, want %q", tr.req.URL.String(), urlStr) } if tr.req.Header == nil { t.Fatalf("expected non-nil request Header") @@ -281,7 +282,7 @@ func TestClientWrites(t *testing.T) { } writes = 0 - _, err = c.PostForm(ts.URL, Values{"foo": {"bar"}}) + _, err = c.PostForm(ts.URL, url.Values{"foo": {"bar"}}) if err != nil { t.Fatal(err) } diff --git a/src/pkg/http/fs_test.go b/src/pkg/http/fs_test.go index 823770ec4..bb6d0158b 100644 --- a/src/pkg/http/fs_test.go +++ b/src/pkg/http/fs_test.go @@ -13,6 +13,7 @@ import ( "path/filepath" "strings" "testing" + "url" ) const ( @@ -49,7 +50,7 @@ func TestServeFile(t *testing.T) { // set up the Request (re-used for all tests) var req Request req.Header = make(Header) - if req.URL, err = ParseURL(ts.URL); err != nil { + if req.URL, err = url.Parse(ts.URL); err != nil { t.Fatal("ParseURL:", err) } req.Method = "GET" diff --git a/src/pkg/http/readrequest_test.go b/src/pkg/http/readrequest_test.go index 79f8de70d..f6dc99e2e 100644 --- a/src/pkg/http/readrequest_test.go +++ b/src/pkg/http/readrequest_test.go @@ -10,6 +10,7 @@ import ( "fmt" "io" "testing" + "url" ) type reqTest struct { @@ -40,7 +41,7 @@ var reqTests = []reqTest{ &Request{ Method: "GET", RawURL: "http://www.techcrunch.com/", - URL: &URL{ + URL: &url.URL{ Raw: "http://www.techcrunch.com/", Scheme: "http", RawPath: "/", @@ -67,7 +68,7 @@ var reqTests = []reqTest{ Close: false, ContentLength: 7, Host: "www.techcrunch.com", - Form: Values{}, + Form: url.Values{}, }, "abcdef\n", @@ -83,7 +84,7 @@ var reqTests = []reqTest{ &Request{ Method: "GET", RawURL: "/", - URL: &URL{ + URL: &url.URL{ Raw: "/", Path: "/", RawPath: "/", @@ -94,7 +95,7 @@ var reqTests = []reqTest{ Close: false, ContentLength: 0, Host: "foo.com", - Form: Values{}, + Form: url.Values{}, }, noBody, @@ -110,7 +111,7 @@ var reqTests = []reqTest{ &Request{ Method: "GET", RawURL: "//user@host/is/actually/a/path/", - URL: &URL{ + URL: &url.URL{ Raw: "//user@host/is/actually/a/path/", Scheme: "", RawPath: "//user@host/is/actually/a/path/", @@ -128,7 +129,7 @@ var reqTests = []reqTest{ Close: false, ContentLength: 0, Host: "test", - Form: Values{}, + Form: url.Values{}, }, noBody, diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go index a1c98a1f8..ed41fa45c 100644 --- a/src/pkg/http/request.go +++ b/src/pkg/http/request.go @@ -22,6 +22,7 @@ import ( "os" "strconv" "strings" + "url" ) const ( @@ -72,9 +73,9 @@ var reqWriteExcludeHeader = map[string]bool{ // A Request represents a parsed HTTP request header. type Request struct { - Method string // GET, POST, PUT, etc. - RawURL string // The raw URL given in the request. - URL *URL // Parsed URL. + Method string // GET, POST, PUT, etc. + RawURL string // The raw URL given in the request. + URL *url.URL // Parsed URL. // The protocol version for incoming requests. // Outgoing requests always use HTTP/1.1. @@ -124,7 +125,7 @@ type Request struct { Host string // The parsed form. Only available after ParseForm is called. - Form Values + Form url.Values // The parsed multipart form, including file uploads. // Only available after ParseMultipartForm is called. @@ -289,22 +290,22 @@ func (req *Request) write(w io.Writer, usingProxy bool) os.Error { host = req.URL.Host } - uri := req.RawURL - if uri == "" { - uri = valueOrDefault(urlEscape(req.URL.Path, encodePath), "/") + urlStr := req.RawURL + if urlStr == "" { + urlStr = valueOrDefault(req.URL.EncodedPath(), "/") if req.URL.RawQuery != "" { - uri += "?" + req.URL.RawQuery + urlStr += "?" + req.URL.RawQuery } if usingProxy { - if uri == "" || uri[0] != '/' { - uri = "/" + uri + if urlStr == "" || urlStr[0] != '/' { + urlStr = "/" + urlStr } - uri = req.URL.Scheme + "://" + host + uri + urlStr = req.URL.Scheme + "://" + host + urlStr } } bw := bufio.NewWriter(w) - fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), uri) + fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), urlStr) // Header lines fmt.Fprintf(bw, "Host: %s\r\n", host) @@ -481,8 +482,8 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err os.Error) { } // NewRequest returns a new Request given a method, URL, and optional body. -func NewRequest(method, url string, body io.Reader) (*Request, os.Error) { - u, err := ParseURL(url) +func NewRequest(method, urlStr string, body io.Reader) (*Request, os.Error) { + u, err := url.Parse(urlStr) if err != nil { return nil, err } @@ -547,7 +548,7 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { return nil, &badStringError{"malformed HTTP version", req.Proto} } - if req.URL, err = ParseRequestURL(req.RawURL); err != nil { + if req.URL, err = url.ParseRequest(req.RawURL); err != nil { return nil, err } @@ -607,77 +608,6 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { return req, nil } -// Values maps a string key to a list of values. -// It is typically used for query parameters and form values. -// Unlike in the Header map, the keys in a Values map -// are case-sensitive. -type Values map[string][]string - -// Get gets the first value associated with the given key. -// If there are no values associated with the key, Get returns -// the empty string. To access multiple values, use the map -// directly. -func (v Values) Get(key string) string { - if v == nil { - return "" - } - vs, ok := v[key] - if !ok || len(vs) == 0 { - return "" - } - return vs[0] -} - -// Set sets the key to value. It replaces any existing -// values. -func (v Values) Set(key, value string) { - v[key] = []string{value} -} - -// Add adds the key to value. It appends to any existing -// values associated with key. -func (v Values) Add(key, value string) { - v[key] = append(v[key], value) -} - -// Del deletes the values associated with key. -func (v Values) Del(key string) { - v[key] = nil, false -} - -// ParseQuery parses the URL-encoded query string and returns -// a map listing the values specified for each key. -// ParseQuery always returns a non-nil map containing all the -// valid query parameters found; err describes the first decoding error -// encountered, if any. -func ParseQuery(query string) (m Values, err os.Error) { - m = make(Values) - err = parseQuery(m, query) - return -} - -func parseQuery(m Values, query string) (err os.Error) { - for _, kv := range strings.Split(query, "&") { - if len(kv) == 0 { - continue - } - kvPair := strings.SplitN(kv, "=", 2) - - var key, value string - var e os.Error - key, e = URLUnescape(kvPair[0]) - if e == nil && len(kvPair) > 1 { - value, e = URLUnescape(kvPair[1]) - } - if e != nil { - err = e - continue - } - m[key] = append(m[key], value) - } - return err -} - // ParseForm parses the raw query. // For POST requests, it also parses the request body as a form. // ParseMultipartForm calls ParseForm automatically. @@ -687,9 +617,8 @@ func (r *Request) ParseForm() (err os.Error) { return } - r.Form = make(Values) if r.URL != nil { - err = parseQuery(r.Form, r.URL.RawQuery) + r.Form, err = url.ParseQuery(r.URL.RawQuery) } if r.Method == "POST" { if r.Body == nil { @@ -709,10 +638,20 @@ func (r *Request) ParseForm() (err os.Error) { if int64(len(b)) > maxFormSize { return os.NewError("http: POST too large") } - e = parseQuery(r.Form, string(b)) + var newValues url.Values + newValues, e = url.ParseQuery(string(b)) if err == nil { err = e } + if r.Form == nil { + r.Form = make(url.Values) + } + // Copy values into r.Form. TODO: make this smoother. + for k, vs := range newValues { + for _, value := range vs { + r.Form.Add(k, value) + } + } case "multipart/form-data": // handled by ParseMultipartForm default: @@ -729,6 +668,9 @@ func (r *Request) ParseForm() (err os.Error) { // ParseMultipartForm calls ParseForm if necessary. // After one call to ParseMultipartForm, subsequent calls have no effect. func (r *Request) ParseMultipartForm(maxMemory int64) os.Error { + if r.MultipartForm == multipartByReader { + return os.NewError("http: multipart handled by MultipartReader") + } if r.Form == nil { err := r.ParseForm() if err != nil { @@ -738,9 +680,6 @@ func (r *Request) ParseMultipartForm(maxMemory int64) os.Error { if r.MultipartForm != nil { return nil } - if r.MultipartForm == multipartByReader { - return os.NewError("http: multipart handled by MultipartReader") - } mr, err := r.multipartReader() if err == ErrNotMultipart { diff --git a/src/pkg/http/request_test.go b/src/pkg/http/request_test.go index e03ed3b05..869cd57b6 100644 --- a/src/pkg/http/request_test.go +++ b/src/pkg/http/request_test.go @@ -17,6 +17,7 @@ import ( "regexp" "strings" "testing" + "url" ) type stringMultimap map[string][]string @@ -43,7 +44,7 @@ var parseTests = []parseTest{ func TestParseForm(t *testing.T) { for i, test := range parseTests { - form, err := ParseQuery(test.query) + form, err := url.ParseQuery(test.query) if err != nil { t.Errorf("test %d: Unexpected error: %v", i, err) continue @@ -72,7 +73,7 @@ func TestParseForm(t *testing.T) { func TestQuery(t *testing.T) { req := &Request{Method: "GET"} - req.URL, _ = ParseURL("http://www.google.com/search?q=foo&q=bar") + req.URL, _ = url.Parse("http://www.google.com/search?q=foo&q=bar") if q := req.FormValue("q"); q != "foo" { t.Errorf(`req.FormValue("q") = %q, want "foo"`, q) } @@ -80,7 +81,7 @@ func TestQuery(t *testing.T) { func TestPostQuery(t *testing.T) { req := &Request{Method: "POST"} - req.URL, _ = ParseURL("http://www.google.com/search?q=foo&q=bar&both=x") + req.URL, _ = url.Parse("http://www.google.com/search?q=foo&q=bar&both=x") req.Header = Header{ "Content-Type": {"application/x-www-form-urlencoded; boo!"}, } @@ -219,6 +220,18 @@ func TestEmptyMultipartRequest(t *testing.T) { testMissingFile(t, req) } +func TestRequestMultipartCallOrder(t *testing.T) { + req := newTestMultipartRequest(t) + _, err := req.MultipartReader() + if err != nil { + t.Fatalf("MultipartReader: %v", err) + } + err = req.ParseMultipartForm(1024) + if err == nil { + t.Errorf("expected an error from ParseMultipartForm after call to MultipartReader") + } +} + func testMissingFile(t *testing.T, req *Request) { f, fh, err := req.FormFile("missing") if f != nil { diff --git a/src/pkg/http/requestwrite_test.go b/src/pkg/http/requestwrite_test.go index 0052c0cfc..458f0bd7f 100644 --- a/src/pkg/http/requestwrite_test.go +++ b/src/pkg/http/requestwrite_test.go @@ -12,6 +12,7 @@ import ( "os" "strings" "testing" + "url" ) type reqWriteTest struct { @@ -27,7 +28,7 @@ var reqWriteTests = []reqWriteTest{ Request{ Method: "GET", RawURL: "http://www.techcrunch.com/", - URL: &URL{ + URL: &url.URL{ Raw: "http://www.techcrunch.com/", Scheme: "http", RawPath: "http://www.techcrunch.com/", @@ -82,7 +83,7 @@ var reqWriteTests = []reqWriteTest{ { Request{ Method: "GET", - URL: &URL{ + URL: &url.URL{ Scheme: "http", Host: "www.google.com", Path: "/search", @@ -111,7 +112,7 @@ var reqWriteTests = []reqWriteTest{ { Request{ Method: "POST", - URL: &URL{ + URL: &url.URL{ Scheme: "http", Host: "www.google.com", Path: "/search", @@ -144,7 +145,7 @@ var reqWriteTests = []reqWriteTest{ { Request{ Method: "POST", - URL: &URL{ + URL: &url.URL{ Scheme: "http", Host: "www.google.com", Path: "/search", diff --git a/src/pkg/http/reverseproxy.go b/src/pkg/http/reverseproxy.go index 015f87f24..3f8bfdc80 100644 --- a/src/pkg/http/reverseproxy.go +++ b/src/pkg/http/reverseproxy.go @@ -14,6 +14,7 @@ import ( "strings" "sync" "time" + "url" ) // ReverseProxy is an HTTP Handler that takes an incoming request and @@ -53,7 +54,7 @@ func singleJoiningSlash(a, b string) string { // URLs to the scheme, host, and base path provided in target. If the // target's path is "/base" and the incoming request was for "/dir", // the target request will be for /base/dir. -func NewSingleHostReverseProxy(target *URL) *ReverseProxy { +func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy { director := func(req *Request) { req.URL.Scheme = target.Scheme req.URL.Host = target.Host diff --git a/src/pkg/http/reverseproxy_test.go b/src/pkg/http/reverseproxy_test.go index b2dd24633..8078c8d10 100644 --- a/src/pkg/http/reverseproxy_test.go +++ b/src/pkg/http/reverseproxy_test.go @@ -11,6 +11,7 @@ import ( "http/httptest" "io/ioutil" "testing" + "url" ) func TestReverseProxy(t *testing.T) { @@ -32,7 +33,7 @@ func TestReverseProxy(t *testing.T) { w.Write([]byte(backendResponse)) })) defer backend.Close() - backendURL, err := ParseURL(backend.URL) + backendURL, err := url.Parse(backend.URL) if err != nil { t.Fatal(err) } diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go index 2725c3b42..ac0403345 100644 --- a/src/pkg/http/serve_test.go +++ b/src/pkg/http/serve_test.go @@ -22,6 +22,7 @@ import ( "syscall" "testing" "time" + "url" ) type dummyAddr string @@ -183,7 +184,7 @@ func TestHostHandlers(t *testing.T) { for _, vt := range vtests { var r *Response var req Request - if req.URL, err = ParseURL(vt.url); err != nil { + if req.URL, err = url.Parse(vt.url); err != nil { t.Errorf("cannot parse url: %v", err) continue } diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go index 1955b67e6..b634e27d6 100644 --- a/src/pkg/http/server.go +++ b/src/pkg/http/server.go @@ -25,6 +25,7 @@ import ( "strings" "sync" "time" + "url" ) // Errors introduced by the HTTP server. @@ -716,8 +717,8 @@ func StripPrefix(prefix string, h Handler) Handler { // Redirect replies to the request with a redirect to url, // which may be a path relative to the request path. -func Redirect(w ResponseWriter, r *Request, url string, code int) { - if u, err := ParseURL(url); err == nil { +func Redirect(w ResponseWriter, r *Request, urlStr string, code int) { + if u, err := url.Parse(urlStr); err == nil { // If url was relative, make absolute by // combining with request path. // The browser would probably do this for us, @@ -740,35 +741,35 @@ func Redirect(w ResponseWriter, r *Request, url string, code int) { } if u.Scheme == "" { // no leading http://server - if url == "" || url[0] != '/' { + if urlStr == "" || urlStr[0] != '/' { // make relative path absolute olddir, _ := path.Split(oldpath) - url = olddir + url + urlStr = olddir + urlStr } var query string - if i := strings.Index(url, "?"); i != -1 { - url, query = url[:i], url[i:] + if i := strings.Index(urlStr, "?"); i != -1 { + urlStr, query = urlStr[:i], urlStr[i:] } // clean up but preserve trailing slash - trailing := url[len(url)-1] == '/' - url = path.Clean(url) - if trailing && url[len(url)-1] != '/' { - url += "/" + trailing := urlStr[len(urlStr)-1] == '/' + urlStr = path.Clean(urlStr) + if trailing && urlStr[len(urlStr)-1] != '/' { + urlStr += "/" } - url += query + urlStr += query } } - w.Header().Set("Location", url) + w.Header().Set("Location", urlStr) w.WriteHeader(code) // RFC2616 recommends that a short note "SHOULD" be included in the // response because older user agents may not understand 301/307. // Shouldn't send the response for POST or HEAD; that leaves GET. if r.Method == "GET" { - note := "" + statusText[code] + ".\n" + note := "" + statusText[code] + ".\n" fmt.Fprintln(w, note) } } diff --git a/src/pkg/http/transport.go b/src/pkg/http/transport.go index 3c16c880d..4302ffab1 100644 --- a/src/pkg/http/transport.go +++ b/src/pkg/http/transport.go @@ -17,6 +17,7 @@ import ( "os" "strings" "sync" + "url" ) // DefaultTransport is the default implementation of Transport and is @@ -46,7 +47,7 @@ type Transport struct { // Request. If the function returns a non-nil error, the // request is aborted with the provided error. // If Proxy is nil or returns a nil *URL, no proxy is used. - Proxy func(*Request) (*URL, os.Error) + Proxy func(*Request) (*url.URL, os.Error) // Dial specifies the dial function for creating TCP // connections. @@ -66,7 +67,7 @@ type Transport struct { // given request, as indicated by the environment variables // $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy). // Either URL or an error is returned. -func ProxyFromEnvironment(req *Request) (*URL, os.Error) { +func ProxyFromEnvironment(req *Request) (*url.URL, os.Error) { proxy := getenvEitherCase("HTTP_PROXY") if proxy == "" { return nil, nil @@ -74,12 +75,12 @@ func ProxyFromEnvironment(req *Request) (*URL, os.Error) { if !useProxy(canonicalAddr(req.URL)) { return nil, nil } - proxyURL, err := ParseRequestURL(proxy) + proxyURL, err := url.ParseRequest(proxy) if err != nil { return nil, os.NewError("invalid proxy address") } if proxyURL.Host == "" { - proxyURL, err = ParseRequestURL("http://" + proxy) + proxyURL, err = url.ParseRequest("http://" + proxy) if err != nil { return nil, os.NewError("invalid proxy address") } @@ -89,16 +90,16 @@ func ProxyFromEnvironment(req *Request) (*URL, os.Error) { // ProxyURL returns a proxy function (for use in a Transport) // that always returns the same URL. -func ProxyURL(url *URL) func(*Request) (*URL, os.Error) { - return func(*Request) (*URL, os.Error) { - return url, nil +func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, os.Error) { + return func(*Request) (*url.URL, os.Error) { + return fixedURL, nil } } // RoundTrip implements the RoundTripper interface. func (t *Transport) RoundTrip(req *Request) (resp *Response, err os.Error) { if req.URL == nil { - if req.URL, err = ParseURL(req.RawURL); err != nil { + if req.URL, err = url.Parse(req.RawURL); err != nil { return } } @@ -413,9 +414,9 @@ func useProxy(addr string) bool { // Note: no support to https to the proxy yet. // type connectMethod struct { - proxyURL *URL // "" for no proxy, else full proxy URL - targetScheme string // "http" or "https" - targetAddr string // Not used if proxy + http targetScheme (4th example in table) + proxyURL *url.URL // nil for no proxy, else full proxy URL + targetScheme string // "http" or "https" + targetAddr string // Not used if proxy + http targetScheme (4th example in table) } func (ck *connectMethod) String() string { @@ -511,6 +512,9 @@ func (pc *persistConn) readLoop() { if err != nil || resp.ContentLength == 0 { return resp, err } + if rc.addedGzip { + forReq.Header.Del("Accept-Encoding") + } if rc.addedGzip && resp.Header.Get("Content-Encoding") == "gzip" { resp.Header.Del("Content-Encoding") resp.Header.Del("Content-Length") @@ -639,7 +643,7 @@ var portMap = map[string]string{ } // canonicalAddr returns url.Host but always with a ":port" suffix -func canonicalAddr(url *URL) string { +func canonicalAddr(url *url.URL) string { addr := url.Host if !hasPort(addr) { return addr + ":" + portMap[url.Scheme] diff --git a/src/pkg/http/transport_test.go b/src/pkg/http/transport_test.go index 76e97640e..eafde7f89 100644 --- a/src/pkg/http/transport_test.go +++ b/src/pkg/http/transport_test.go @@ -20,6 +20,7 @@ import ( "strings" "testing" "time" + "url" ) // TODO: test 5 pipelined requests with responses: 1) OK, 2) OK, Connection: Close @@ -77,7 +78,7 @@ func TestTransportConnectionCloseOnResponse(t *testing.T) { fetch := func(n int) string { req := new(Request) var err os.Error - req.URL, err = ParseURL(ts.URL + fmt.Sprintf("?close=%v", connectionClose)) + req.URL, err = url.Parse(ts.URL + fmt.Sprintf("?close=%v", connectionClose)) if err != nil { t.Fatalf("URL parse error: %v", err) } @@ -119,7 +120,7 @@ func TestTransportConnectionCloseOnRequest(t *testing.T) { fetch := func(n int) string { req := new(Request) var err os.Error - req.URL, err = ParseURL(ts.URL) + req.URL, err = url.Parse(ts.URL) if err != nil { t.Fatalf("URL parse error: %v", err) } @@ -387,6 +388,68 @@ func TestTransportNilURL(t *testing.T) { } } +var roundTripTests = []struct { + accept string + expectAccept string + compressed bool +}{ + // Requests with no accept-encoding header use transparent compression + {"", "gzip", false}, + // Requests with other accept-encoding should pass through unmodified + {"foo", "foo", false}, + // Requests with accept-encoding == gzip should be passed through + {"gzip", "gzip", true}} + +// Test that the modification made to the Request by the RoundTripper is cleaned up +func TestRoundTripGzip(t *testing.T) { + const responseBody = "test response body" + ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { + accept := req.Header.Get("Accept-Encoding") + if expect := req.FormValue("expect_accept"); accept != expect { + t.Errorf("Accept-Encoding = %q, want %q", accept, expect) + } + if accept == "gzip" { + rw.Header().Set("Content-Encoding", "gzip") + gz, _ := gzip.NewWriter(rw) + gz.Write([]byte(responseBody)) + gz.Close() + } else { + rw.Header().Set("Content-Encoding", accept) + rw.Write([]byte(responseBody)) + } + })) + defer ts.Close() + + for i, test := range roundTripTests { + // Test basic request (no accept-encoding) + req, _ := NewRequest("GET", ts.URL+"?expect_accept="+test.expectAccept, nil) + req.Header.Set("Accept-Encoding", test.accept) + res, err := DefaultTransport.RoundTrip(req) + var body []byte + if test.compressed { + gzip, _ := gzip.NewReader(res.Body) + body, err = ioutil.ReadAll(gzip) + res.Body.Close() + } else { + body, err = ioutil.ReadAll(res.Body) + } + if err != nil { + t.Errorf("%d. Error: %q", i, err) + } else { + if g, e := string(body), responseBody; g != e { + t.Errorf("%d. body = %q; want %q", i, g, e) + } + if g, e := req.Header.Get("Accept-Encoding"), test.accept; g != e { + t.Errorf("%d. Accept-Encoding = %q; want %q", i, g, e) + } + if g, e := res.Header.Get("Content-Encoding"), test.accept; g != e { + t.Errorf("%d. Content-Encoding = %q; want %q", i, g, e) + } + } + } + +} + func TestTransportGzip(t *testing.T) { const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" const nRandBytes = 1024 * 1024 @@ -490,7 +553,7 @@ func TestTransportProxy(t *testing.T) { })) defer proxy.Close() - pu, err := ParseURL(proxy.URL) + pu, err := url.Parse(proxy.URL) if err != nil { t.Fatal(err) } diff --git a/src/pkg/http/url.go b/src/pkg/http/url.go deleted file mode 100644 index b38585ac2..000000000 --- a/src/pkg/http/url.go +++ /dev/null @@ -1,606 +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. - -// Parse URLs (actually URIs, but that seems overly pedantic). -// RFC 3986 - -package http - -import ( - "os" - "strconv" - "strings" -) - -// URLError reports an error and the operation and URL that caused it. -type URLError struct { - Op string - URL string - Error os.Error -} - -func (e *URLError) String() string { return e.Op + " " + e.URL + ": " + e.Error.String() } - -func ishex(c byte) bool { - switch { - case '0' <= c && c <= '9': - return true - case 'a' <= c && c <= 'f': - return true - case 'A' <= c && c <= 'F': - return true - } - return false -} - -func unhex(c byte) byte { - switch { - case '0' <= c && c <= '9': - return c - '0' - case 'a' <= c && c <= 'f': - return c - 'a' + 10 - case 'A' <= c && c <= 'F': - return c - 'A' + 10 - } - return 0 -} - -type encoding int - -const ( - encodePath encoding = 1 + iota - encodeUserPassword - encodeQueryComponent - encodeFragment - encodeOpaque -) - -type URLEscapeError string - -func (e URLEscapeError) String() string { - return "invalid URL escape " + strconv.Quote(string(e)) -} - -// Return true if the specified character should be escaped when -// appearing in a URL string, according to RFC 2396. -// When 'all' is true the full range of reserved characters are matched. -func shouldEscape(c byte, mode encoding) bool { - // RFC 2396 §2.3 Unreserved characters (alphanum) - if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { - return false - } - switch c { - case '-', '_', '.', '!', '~', '*', '\'', '(', ')': // §2.3 Unreserved characters (mark) - return false - - case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved) - // Different sections of the URL allow a few of - // the reserved characters to appear unescaped. - switch mode { - case encodePath: // §3.3 - // The RFC allows : @ & = + $ , but saves / ; for assigning - // meaning to individual path segments. This package - // only manipulates the path as a whole, so we allow those - // last two as well. Clients that need to distinguish between - // `/foo;y=z/bar` and `/foo%3by=z/bar` will have to re-decode RawPath. - // That leaves only ? to escape. - return c == '?' - - case encodeUserPassword: // §3.2.2 - // The RFC allows ; : & = + $ , in userinfo, so we must escape only @ and /. - // The parsing of userinfo treats : as special so we must escape that too. - return c == '@' || c == '/' || c == ':' - - case encodeQueryComponent: // §3.4 - // The RFC reserves (so we must escape) everything. - return true - - case encodeFragment: // §4.1 - // The RFC text is silent but the grammar allows - // everything, so escape nothing. - return false - - case encodeOpaque: // §3 opaque_part - // The RFC allows opaque_part to use all characters - // except that the leading / must be escaped. - // (We implement that case in String.) - return false - } - } - - // Everything else must be escaped. - return true -} - -// URLUnescape unescapes a string in ``URL encoded'' form, -// converting %AB into the byte 0xAB and '+' into ' ' (space). -// It returns an error if any % is not followed -// by two hexadecimal digits. -// Despite the name, this encoding applies only to individual -// components of the query portion of the URL. -func URLUnescape(s string) (string, os.Error) { - return urlUnescape(s, encodeQueryComponent) -} - -// urlUnescape is like URLUnescape but mode specifies -// which section of the URL is being unescaped. -func urlUnescape(s string, mode encoding) (string, os.Error) { - // Count %, check that they're well-formed. - n := 0 - hasPlus := false - for i := 0; i < len(s); { - switch s[i] { - case '%': - n++ - if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { - s = s[i:] - if len(s) > 3 { - s = s[0:3] - } - return "", URLEscapeError(s) - } - i += 3 - case '+': - hasPlus = mode == encodeQueryComponent - i++ - default: - i++ - } - } - - if n == 0 && !hasPlus { - return s, nil - } - - t := make([]byte, len(s)-2*n) - j := 0 - for i := 0; i < len(s); { - switch s[i] { - case '%': - t[j] = unhex(s[i+1])<<4 | unhex(s[i+2]) - j++ - i += 3 - case '+': - if mode == encodeQueryComponent { - t[j] = ' ' - } else { - t[j] = '+' - } - j++ - i++ - default: - t[j] = s[i] - j++ - i++ - } - } - return string(t), nil -} - -// URLEscape converts a string into ``URL encoded'' form. -// Despite the name, this encoding applies only to individual -// components of the query portion of the URL. -func URLEscape(s string) string { - return urlEscape(s, encodeQueryComponent) -} - -func urlEscape(s string, mode encoding) string { - spaceCount, hexCount := 0, 0 - for i := 0; i < len(s); i++ { - c := s[i] - if shouldEscape(c, mode) { - if c == ' ' && mode == encodeQueryComponent { - spaceCount++ - } else { - hexCount++ - } - } - } - - if spaceCount == 0 && hexCount == 0 { - return s - } - - t := make([]byte, len(s)+2*hexCount) - j := 0 - for i := 0; i < len(s); i++ { - switch c := s[i]; { - case c == ' ' && mode == encodeQueryComponent: - t[j] = '+' - j++ - case shouldEscape(c, mode): - t[j] = '%' - t[j+1] = "0123456789ABCDEF"[c>>4] - t[j+2] = "0123456789ABCDEF"[c&15] - j += 3 - default: - t[j] = s[i] - j++ - } - } - return string(t) -} - -// UnescapeUserinfo parses the RawUserinfo field of a URL -// as the form user or user:password and unescapes and returns -// the two halves. -// -// This functionality should only be used with legacy web sites. -// RFC 2396 warns that interpreting Userinfo this way -// ``is NOT RECOMMENDED, because the passing of authentication -// information in clear text (such as URI) has proven to be a -// security risk in almost every case where it has been used.'' -func UnescapeUserinfo(rawUserinfo string) (user, password string, err os.Error) { - u, p := split(rawUserinfo, ':', true) - if user, err = urlUnescape(u, encodeUserPassword); err != nil { - return "", "", err - } - if password, err = urlUnescape(p, encodeUserPassword); err != nil { - return "", "", err - } - return -} - -// EscapeUserinfo combines user and password in the form -// user:password (or just user if password is empty) and then -// escapes it for use as the URL.RawUserinfo field. -// -// This functionality should only be used with legacy web sites. -// RFC 2396 warns that interpreting Userinfo this way -// ``is NOT RECOMMENDED, because the passing of authentication -// information in clear text (such as URI) has proven to be a -// security risk in almost every case where it has been used.'' -func EscapeUserinfo(user, password string) string { - raw := urlEscape(user, encodeUserPassword) - if password != "" { - raw += ":" + urlEscape(password, encodeUserPassword) - } - return raw -} - -// A URL represents a parsed URL (technically, a URI reference). -// The general form represented is: -// scheme://[userinfo@]host/path[?query][#fragment] -// The Raw, RawAuthority, RawPath, and RawQuery fields are in "wire format" -// (special characters must be hex-escaped if not meant to have special meaning). -// All other fields are logical values; '+' or '%' represent themselves. -// -// The various Raw values are supplied in wire format because -// clients typically have to split them into pieces before further -// decoding. -type URL struct { - Raw string // the original string - Scheme string // scheme - RawAuthority string // [userinfo@]host - RawUserinfo string // userinfo - Host string // host - RawPath string // /path[?query][#fragment] - Path string // /path - OpaquePath bool // path is opaque (unrooted when scheme is present) - RawQuery string // query - Fragment string // fragment -} - -// Maybe rawurl is of the form scheme:path. -// (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*) -// If so, return scheme, path; else return "", rawurl. -func getscheme(rawurl string) (scheme, path string, err os.Error) { - for i := 0; i < len(rawurl); i++ { - c := rawurl[i] - switch { - case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': - // do nothing - case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.': - if i == 0 { - return "", rawurl, nil - } - case c == ':': - if i == 0 { - return "", "", os.NewError("missing protocol scheme") - } - return rawurl[0:i], rawurl[i+1:], nil - default: - // we have encountered an invalid character, - // so there is no valid scheme - return "", rawurl, nil - } - } - return "", rawurl, nil -} - -// Maybe s is of the form t c u. -// If so, return t, c u (or t, u if cutc == true). -// If not, return s, "". -func split(s string, c byte, cutc bool) (string, string) { - for i := 0; i < len(s); i++ { - if s[i] == c { - if cutc { - return s[0:i], s[i+1:] - } - return s[0:i], s[i:] - } - } - return s, "" -} - -// ParseURL parses rawurl into a URL structure. -// The string rawurl is assumed not to have a #fragment suffix. -// (Web browsers strip #fragment before sending the URL to a web server.) -// The rawurl may be relative or absolute. -func ParseURL(rawurl string) (url *URL, err os.Error) { - return parseURL(rawurl, false) -} - -// ParseRequestURL parses rawurl into a URL structure. It assumes that -// rawurl was received from an HTTP request, so the rawurl is interpreted -// only as an absolute URI or an absolute path. -// The string rawurl is assumed not to have a #fragment suffix. -// (Web browsers strip #fragment before sending the URL to a web server.) -func ParseRequestURL(rawurl string) (url *URL, err os.Error) { - return parseURL(rawurl, true) -} - -// parseURL parses a URL from a string in one of two contexts. If -// viaRequest is true, the URL is assumed to have arrived via an HTTP request, -// in which case only absolute URLs or path-absolute relative URLs are allowed. -// If viaRequest is false, all forms of relative URLs are allowed. -func parseURL(rawurl string, viaRequest bool) (url *URL, err os.Error) { - var ( - leadingSlash bool - path string - ) - - if rawurl == "" { - err = os.NewError("empty url") - goto Error - } - url = new(URL) - url.Raw = rawurl - - // Split off possible leading "http:", "mailto:", etc. - // Cannot contain escaped characters. - if url.Scheme, path, err = getscheme(rawurl); err != nil { - goto Error - } - leadingSlash = strings.HasPrefix(path, "/") - - if url.Scheme != "" && !leadingSlash { - // RFC 2396: - // Absolute URI (has scheme) with non-rooted path - // is uninterpreted. It doesn't even have a ?query. - // This is the case that handles mailto:name@example.com. - url.RawPath = path - - if url.Path, err = urlUnescape(path, encodeOpaque); err != nil { - goto Error - } - url.OpaquePath = true - } else { - if viaRequest && !leadingSlash { - err = os.NewError("invalid URI for request") - goto Error - } - - // Split off query before parsing path further. - url.RawPath = path - path, query := split(path, '?', false) - if len(query) > 1 { - url.RawQuery = query[1:] - } - - // Maybe path is //authority/path - if (url.Scheme != "" || !viaRequest) && - strings.HasPrefix(path, "//") && !strings.HasPrefix(path, "///") { - url.RawAuthority, path = split(path[2:], '/', false) - url.RawPath = url.RawPath[2+len(url.RawAuthority):] - } - - // Split authority into userinfo@host. - // If there's no @, split's default is wrong. Check explicitly. - var rawHost string - if strings.Index(url.RawAuthority, "@") < 0 { - rawHost = url.RawAuthority - } else { - url.RawUserinfo, rawHost = split(url.RawAuthority, '@', true) - } - - // We leave RawAuthority only in raw form because clients - // of common protocols should be using Userinfo and Host - // instead. Clients that wish to use RawAuthority will have to - // interpret it themselves: RFC 2396 does not define the meaning. - - if strings.Contains(rawHost, "%") { - // Host cannot contain escaped characters. - err = os.NewError("hexadecimal escape in host") - goto Error - } - url.Host = rawHost - - if url.Path, err = urlUnescape(path, encodePath); err != nil { - goto Error - } - } - return url, nil - -Error: - return nil, &URLError{"parse", rawurl, err} - -} - -// ParseURLReference is like ParseURL but allows a trailing #fragment. -func ParseURLReference(rawurlref string) (url *URL, err os.Error) { - // Cut off #frag. - rawurl, frag := split(rawurlref, '#', false) - if url, err = ParseURL(rawurl); err != nil { - return nil, err - } - url.Raw += frag - url.RawPath += frag - if len(frag) > 1 { - frag = frag[1:] - if url.Fragment, err = urlUnescape(frag, encodeFragment); err != nil { - return nil, &URLError{"parse", rawurl, err} - } - } - return url, nil -} - -// String reassembles url into a valid URL string. -// -// There are redundant fields stored in the URL structure: -// the String method consults Scheme, Path, Host, RawUserinfo, -// RawQuery, and Fragment, but not Raw, RawPath or RawAuthority. -func (url *URL) String() string { - result := "" - if url.Scheme != "" { - result += url.Scheme + ":" - } - if url.Host != "" || url.RawUserinfo != "" { - result += "//" - if url.RawUserinfo != "" { - // hide the password, if any - info := url.RawUserinfo - if i := strings.Index(info, ":"); i >= 0 { - info = info[0:i] + ":******" - } - result += info + "@" - } - result += url.Host - } - if url.OpaquePath { - path := url.Path - if strings.HasPrefix(path, "/") { - result += "%2f" - path = path[1:] - } - result += urlEscape(path, encodeOpaque) - } else { - result += urlEscape(url.Path, encodePath) - } - if url.RawQuery != "" { - result += "?" + url.RawQuery - } - if url.Fragment != "" { - result += "#" + urlEscape(url.Fragment, encodeFragment) - } - return result -} - -// Encode encodes the values into ``URL encoded'' form. -// e.g. "foo=bar&bar=baz" -func (v Values) Encode() string { - if v == nil { - return "" - } - parts := make([]string, 0, len(v)) // will be large enough for most uses - for k, vs := range v { - prefix := URLEscape(k) + "=" - for _, v := range vs { - parts = append(parts, prefix+URLEscape(v)) - } - } - return strings.Join(parts, "&") -} - -// resolvePath applies special path segments from refs and applies -// them to base, per RFC 2396. -func resolvePath(basepath string, refpath string) string { - base := strings.Split(basepath, "/") - refs := strings.Split(refpath, "/") - if len(base) == 0 { - base = []string{""} - } - for idx, ref := range refs { - switch { - case ref == ".": - base[len(base)-1] = "" - case ref == "..": - newLen := len(base) - 1 - if newLen < 1 { - newLen = 1 - } - base = base[0:newLen] - base[len(base)-1] = "" - default: - if idx == 0 || base[len(base)-1] == "" { - base[len(base)-1] = ref - } else { - base = append(base, ref) - } - } - } - return strings.Join(base, "/") -} - -// IsAbs returns true if the URL is absolute. -func (url *URL) IsAbs() bool { - return url.Scheme != "" -} - -// ParseURL parses a URL in the context of a base URL. The URL in ref -// may be relative or absolute. ParseURL returns nil, err on parse -// failure, otherwise its return value is the same as ResolveReference. -func (base *URL) ParseURL(ref string) (*URL, os.Error) { - refurl, err := ParseURL(ref) - if err != nil { - return nil, err - } - return base.ResolveReference(refurl), nil -} - -// ResolveReference resolves a URI reference to an absolute URI from -// an absolute base URI, per RFC 2396 Section 5.2. The URI reference -// may be relative or absolute. ResolveReference always returns a new -// URL instance, even if the returned URL is identical to either the -// base or reference. If ref is an absolute URL, then ResolveReference -// ignores base and returns a copy of ref. -func (base *URL) ResolveReference(ref *URL) *URL { - url := new(URL) - switch { - case ref.IsAbs(): - *url = *ref - default: - // relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] - *url = *base - if ref.RawAuthority != "" { - // The "net_path" case. - url.RawAuthority = ref.RawAuthority - url.Host = ref.Host - url.RawUserinfo = ref.RawUserinfo - } - switch { - case url.OpaquePath: - url.Path = ref.Path - url.RawPath = ref.RawPath - url.RawQuery = ref.RawQuery - case strings.HasPrefix(ref.Path, "/"): - // The "abs_path" case. - url.Path = ref.Path - url.RawPath = ref.RawPath - url.RawQuery = ref.RawQuery - default: - // The "rel_path" case. - path := resolvePath(base.Path, ref.Path) - if !strings.HasPrefix(path, "/") { - path = "/" + path - } - url.Path = path - url.RawPath = url.Path - url.RawQuery = ref.RawQuery - if ref.RawQuery != "" { - url.RawPath += "?" + url.RawQuery - } - } - - url.Fragment = ref.Fragment - } - url.Raw = url.String() - return url -} - -// Query parses RawQuery and returns the corresponding values. -func (u *URL) Query() Values { - v, _ := ParseQuery(u.RawQuery) - return v -} diff --git a/src/pkg/http/url_test.go b/src/pkg/http/url_test.go deleted file mode 100644 index eaec5872a..000000000 --- a/src/pkg/http/url_test.go +++ /dev/null @@ -1,698 +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 http - -import ( - "fmt" - "os" - "reflect" - "testing" -) - -// TODO(rsc): -// test URLUnescape -// test URLEscape -// test ParseURL - -type URLTest struct { - in string - out *URL - roundtrip string // expected result of reserializing the URL; empty means same as "in". -} - -var urltests = []URLTest{ - // no path - { - "http://www.google.com", - &URL{ - Raw: "http://www.google.com", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - }, - "", - }, - // path - { - "http://www.google.com/", - &URL{ - Raw: "http://www.google.com/", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/", - Path: "/", - }, - "", - }, - // path with hex escaping - { - "http://www.google.com/file%20one%26two", - &URL{ - Raw: "http://www.google.com/file%20one%26two", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/file%20one%26two", - Path: "/file one&two", - }, - "http://www.google.com/file%20one&two", - }, - // user - { - "ftp://webmaster@www.google.com/", - &URL{ - Raw: "ftp://webmaster@www.google.com/", - Scheme: "ftp", - RawAuthority: "webmaster@www.google.com", - RawUserinfo: "webmaster", - Host: "www.google.com", - RawPath: "/", - Path: "/", - }, - "", - }, - // escape sequence in username - { - "ftp://john%20doe@www.google.com/", - &URL{ - Raw: "ftp://john%20doe@www.google.com/", - Scheme: "ftp", - RawAuthority: "john%20doe@www.google.com", - RawUserinfo: "john%20doe", - Host: "www.google.com", - RawPath: "/", - Path: "/", - }, - "ftp://john%20doe@www.google.com/", - }, - // query - { - "http://www.google.com/?q=go+language", - &URL{ - Raw: "http://www.google.com/?q=go+language", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language", - Path: "/", - RawQuery: "q=go+language", - }, - "", - }, - // query with hex escaping: NOT parsed - { - "http://www.google.com/?q=go%20language", - &URL{ - Raw: "http://www.google.com/?q=go%20language", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go%20language", - Path: "/", - RawQuery: "q=go%20language", - }, - "", - }, - // %20 outside query - { - "http://www.google.com/a%20b?q=c+d", - &URL{ - Raw: "http://www.google.com/a%20b?q=c+d", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/a%20b?q=c+d", - Path: "/a b", - RawQuery: "q=c+d", - }, - "", - }, - // path without leading /, so no query parsing - { - "http:www.google.com/?q=go+language", - &URL{ - Raw: "http:www.google.com/?q=go+language", - Scheme: "http", - RawPath: "www.google.com/?q=go+language", - Path: "www.google.com/?q=go+language", - OpaquePath: true, - }, - "http:www.google.com/?q=go+language", - }, - // path without leading /, so no query parsing - { - "http:%2f%2fwww.google.com/?q=go+language", - &URL{ - Raw: "http:%2f%2fwww.google.com/?q=go+language", - Scheme: "http", - RawPath: "%2f%2fwww.google.com/?q=go+language", - Path: "//www.google.com/?q=go+language", - OpaquePath: true, - }, - "http:%2f/www.google.com/?q=go+language", - }, - // non-authority - { - "mailto:/webmaster@golang.org", - &URL{ - Raw: "mailto:/webmaster@golang.org", - Scheme: "mailto", - RawPath: "/webmaster@golang.org", - Path: "/webmaster@golang.org", - }, - "", - }, - // non-authority - { - "mailto:webmaster@golang.org", - &URL{ - Raw: "mailto:webmaster@golang.org", - Scheme: "mailto", - RawPath: "webmaster@golang.org", - Path: "webmaster@golang.org", - OpaquePath: true, - }, - "", - }, - // unescaped :// in query should not create a scheme - { - "/foo?query=http://bad", - &URL{ - Raw: "/foo?query=http://bad", - RawPath: "/foo?query=http://bad", - Path: "/foo", - RawQuery: "query=http://bad", - }, - "", - }, - // leading // without scheme should create an authority - { - "//foo", - &URL{ - RawAuthority: "foo", - Raw: "//foo", - Host: "foo", - Scheme: "", - RawPath: "", - Path: "", - }, - "", - }, - // leading // without scheme, with userinfo, path, and query - { - "//user@foo/path?a=b", - &URL{ - Raw: "//user@foo/path?a=b", - RawAuthority: "user@foo", - RawUserinfo: "user", - Scheme: "", - RawPath: "/path?a=b", - Path: "/path", - RawQuery: "a=b", - Host: "foo", - }, - "", - }, - // Three leading slashes isn't an authority, but doesn't return an error. - // (We can't return an error, as this code is also used via - // ServeHTTP -> ReadRequest -> ParseURL, which is arguably a - // different URL parsing context, but currently shares the - // same codepath) - { - "///threeslashes", - &URL{ - RawAuthority: "", - Raw: "///threeslashes", - Host: "", - Scheme: "", - RawPath: "///threeslashes", - Path: "///threeslashes", - }, - "", - }, - { - "http://user:password@google.com", - &URL{ - Raw: "http://user:password@google.com", - Scheme: "http", - RawAuthority: "user:password@google.com", - RawUserinfo: "user:password", - Host: "google.com", - }, - "http://user:******@google.com", - }, - { - "http://user:longerpass@google.com", - &URL{ - Raw: "http://user:longerpass@google.com", - Scheme: "http", - RawAuthority: "user:longerpass@google.com", - RawUserinfo: "user:longerpass", - Host: "google.com", - }, - "http://user:******@google.com", - }, -} - -var urlnofragtests = []URLTest{ - { - "http://www.google.com/?q=go+language#foo", - &URL{ - Raw: "http://www.google.com/?q=go+language#foo", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language#foo", - Path: "/", - RawQuery: "q=go+language#foo", - }, - "", - }, -} - -var urlfragtests = []URLTest{ - { - "http://www.google.com/?q=go+language#foo", - &URL{ - Raw: "http://www.google.com/?q=go+language#foo", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language#foo", - Path: "/", - RawQuery: "q=go+language", - Fragment: "foo", - }, - "", - }, - { - "http://www.google.com/?q=go+language#foo%26bar", - &URL{ - Raw: "http://www.google.com/?q=go+language#foo%26bar", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language#foo%26bar", - Path: "/", - RawQuery: "q=go+language", - Fragment: "foo&bar", - }, - "http://www.google.com/?q=go+language#foo&bar", - }, -} - -// more useful string for debugging than fmt's struct printer -func ufmt(u *URL) string { - return fmt.Sprintf("raw=%q, scheme=%q, rawpath=%q, auth=%q, userinfo=%q, host=%q, path=%q, rawq=%q, frag=%q", - u.Raw, u.Scheme, u.RawPath, u.RawAuthority, u.RawUserinfo, - u.Host, u.Path, u.RawQuery, u.Fragment) -} - -func DoTest(t *testing.T, parse func(string) (*URL, os.Error), name string, tests []URLTest) { - for _, tt := range tests { - u, err := parse(tt.in) - if err != nil { - t.Errorf("%s(%q) returned error %s", name, tt.in, err) - continue - } - if !reflect.DeepEqual(u, tt.out) { - t.Errorf("%s(%q):\n\thave %v\n\twant %v\n", - name, tt.in, ufmt(u), ufmt(tt.out)) - } - } -} - -func TestParseURL(t *testing.T) { - DoTest(t, ParseURL, "ParseURL", urltests) - DoTest(t, ParseURL, "ParseURL", urlnofragtests) -} - -func TestParseURLReference(t *testing.T) { - DoTest(t, ParseURLReference, "ParseURLReference", urltests) - DoTest(t, ParseURLReference, "ParseURLReference", urlfragtests) -} - -const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path" - -var parseRequestUrlTests = []struct { - url string - expectedValid bool -}{ - {"http://foo.com", true}, - {"http://foo.com/", true}, - {"http://foo.com/path", true}, - {"/", true}, - {pathThatLooksSchemeRelative, true}, - {"//not.a.user@%66%6f%6f.com/just/a/path/also", true}, - {"foo.html", false}, - {"../dir/", false}, -} - -func TestParseRequestURL(t *testing.T) { - for _, test := range parseRequestUrlTests { - _, err := ParseRequestURL(test.url) - valid := err == nil - if valid != test.expectedValid { - t.Errorf("Expected valid=%v for %q; got %v", test.expectedValid, test.url, valid) - } - } - - url, err := ParseRequestURL(pathThatLooksSchemeRelative) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if url.Path != pathThatLooksSchemeRelative { - t.Errorf("Expected path %q; got %q", pathThatLooksSchemeRelative, url.Path) - } -} - -func DoTestString(t *testing.T, parse func(string) (*URL, os.Error), name string, tests []URLTest) { - for _, tt := range tests { - u, err := parse(tt.in) - if err != nil { - t.Errorf("%s(%q) returned error %s", name, tt.in, err) - continue - } - s := u.String() - expected := tt.in - if len(tt.roundtrip) > 0 { - expected = tt.roundtrip - } - if s != expected { - t.Errorf("%s(%q).String() == %q (expected %q)", name, tt.in, s, expected) - } - } -} - -func TestURLString(t *testing.T) { - DoTestString(t, ParseURL, "ParseURL", urltests) - DoTestString(t, ParseURL, "ParseURL", urlnofragtests) - DoTestString(t, ParseURLReference, "ParseURLReference", urltests) - DoTestString(t, ParseURLReference, "ParseURLReference", urlfragtests) -} - -type URLEscapeTest struct { - in string - out string - err os.Error -} - -var unescapeTests = []URLEscapeTest{ - { - "", - "", - nil, - }, - { - "abc", - "abc", - nil, - }, - { - "1%41", - "1A", - nil, - }, - { - "1%41%42%43", - "1ABC", - nil, - }, - { - "%4a", - "J", - nil, - }, - { - "%6F", - "o", - nil, - }, - { - "%", // not enough characters after % - "", - URLEscapeError("%"), - }, - { - "%a", // not enough characters after % - "", - URLEscapeError("%a"), - }, - { - "%1", // not enough characters after % - "", - URLEscapeError("%1"), - }, - { - "123%45%6", // not enough characters after % - "", - URLEscapeError("%6"), - }, - { - "%zzzzz", // invalid hex digits - "", - URLEscapeError("%zz"), - }, -} - -func TestURLUnescape(t *testing.T) { - for _, tt := range unescapeTests { - actual, err := URLUnescape(tt.in) - if actual != tt.out || (err != nil) != (tt.err != nil) { - t.Errorf("URLUnescape(%q) = %q, %s; want %q, %s", tt.in, actual, err, tt.out, tt.err) - } - } -} - -var escapeTests = []URLEscapeTest{ - { - "", - "", - nil, - }, - { - "abc", - "abc", - nil, - }, - { - "one two", - "one+two", - nil, - }, - { - "10%", - "10%25", - nil, - }, - { - " ?&=#+%!<>#\"{}|\\^[]`☺\t", - "+%3F%26%3D%23%2B%25!%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09", - nil, - }, -} - -func TestURLEscape(t *testing.T) { - for _, tt := range escapeTests { - actual := URLEscape(tt.in) - if tt.out != actual { - t.Errorf("URLEscape(%q) = %q, want %q", tt.in, actual, tt.out) - } - - // for bonus points, verify that escape:unescape is an identity. - roundtrip, err := URLUnescape(actual) - if roundtrip != tt.in || err != nil { - t.Errorf("URLUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]") - } - } -} - -type UserinfoTest struct { - User string - Password string - Raw string -} - -var userinfoTests = []UserinfoTest{ - {"user", "password", "user:password"}, - {"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./", - "foo%3Abar:~!%40%23$%25%5E&*()_+%7B%7D%7C%5B%5D%5C-=%60%3A;'%22%3C%3E?,.%2F"}, -} - -func TestEscapeUserinfo(t *testing.T) { - for _, tt := range userinfoTests { - if raw := EscapeUserinfo(tt.User, tt.Password); raw != tt.Raw { - t.Errorf("EscapeUserinfo(%q, %q) = %q, want %q", tt.User, tt.Password, raw, tt.Raw) - } - } -} - -func TestUnescapeUserinfo(t *testing.T) { - for _, tt := range userinfoTests { - if user, pass, err := UnescapeUserinfo(tt.Raw); user != tt.User || pass != tt.Password || err != nil { - t.Errorf("UnescapeUserinfo(%q) = %q, %q, %v, want %q, %q, nil", tt.Raw, user, pass, err, tt.User, tt.Password) - } - } -} - -type EncodeQueryTest struct { - m Values - expected string - expected1 string -} - -var encodeQueryTests = []EncodeQueryTest{ - {nil, "", ""}, - {Values{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"}, - {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"}, -} - -func TestEncodeQuery(t *testing.T) { - for _, tt := range encodeQueryTests { - if q := tt.m.Encode(); q != tt.expected && q != tt.expected1 { - t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected) - } - } -} - -var resolvePathTests = []struct { - base, ref, expected string -}{ - {"a/b", ".", "a/"}, - {"a/b", "c", "a/c"}, - {"a/b", "..", ""}, - {"a/", "..", ""}, - {"a/", "../..", ""}, - {"a/b/c", "..", "a/"}, - {"a/b/c", "../d", "a/d"}, - {"a/b/c", ".././d", "a/d"}, - {"a/b", "./..", ""}, - {"a/./b", ".", "a/./"}, - {"a/../", ".", "a/../"}, - {"a/.././b", "c", "a/.././c"}, -} - -func TestResolvePath(t *testing.T) { - for _, test := range resolvePathTests { - got := resolvePath(test.base, test.ref) - if got != test.expected { - t.Errorf("For %q + %q got %q; expected %q", test.base, test.ref, got, test.expected) - } - } -} - -var resolveReferenceTests = []struct { - base, rel, expected string -}{ - // Absolute URL references - {"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"}, - {"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"}, - {"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"}, - - // Path-absolute references - {"http://foo.com/bar", "/baz", "http://foo.com/baz"}, - {"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"}, - {"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"}, - - // Scheme-relative - {"https://foo.com/bar?a=b", "//bar.com/quux", "https://bar.com/quux"}, - - // Path-relative references: - - // ... current directory - {"http://foo.com", ".", "http://foo.com/"}, - {"http://foo.com/bar", ".", "http://foo.com/"}, - {"http://foo.com/bar/", ".", "http://foo.com/bar/"}, - - // ... going down - {"http://foo.com", "bar", "http://foo.com/bar"}, - {"http://foo.com/", "bar", "http://foo.com/bar"}, - {"http://foo.com/bar/baz", "quux", "http://foo.com/bar/quux"}, - - // ... going up - {"http://foo.com/bar/baz", "../quux", "http://foo.com/quux"}, - {"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"}, - {"http://foo.com/bar", "..", "http://foo.com/"}, - {"http://foo.com/bar/baz", "./..", "http://foo.com/"}, - - // "." and ".." in the base aren't special - {"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/./dotdot/../baz"}, - - // Triple dot isn't special - {"http://foo.com/bar", "...", "http://foo.com/..."}, - - // Fragment - {"http://foo.com/bar", ".#frag", "http://foo.com/#frag"}, -} - -func TestResolveReference(t *testing.T) { - mustParseURL := func(url string) *URL { - u, err := ParseURLReference(url) - if err != nil { - t.Fatalf("Expected URL to parse: %q, got error: %v", url, err) - } - return u - } - for _, test := range resolveReferenceTests { - base := mustParseURL(test.base) - rel := mustParseURL(test.rel) - url := base.ResolveReference(rel) - urlStr := url.String() - if urlStr != test.expected { - t.Errorf("Resolving %q + %q != %q; got %q", test.base, test.rel, test.expected, urlStr) - } - } - - // Test that new instances are returned. - base := mustParseURL("http://foo.com/") - abs := base.ResolveReference(mustParseURL(".")) - if base == abs { - t.Errorf("Expected no-op reference to return new URL instance.") - } - barRef := mustParseURL("http://bar.com/") - abs = base.ResolveReference(barRef) - if abs == barRef { - t.Errorf("Expected resolution of absolute reference to return new URL instance.") - } - - // Test the convenience wrapper too - base = mustParseURL("http://foo.com/path/one/") - abs, _ = base.ParseURL("../two") - expected := "http://foo.com/path/two" - if abs.String() != expected { - t.Errorf("ParseURL wrapper got %q; expected %q", abs.String(), expected) - } - _, err := base.ParseURL("") - if err == nil { - t.Errorf("Expected an error from ParseURL wrapper parsing an empty string.") - } - -} - -func TestQueryValues(t *testing.T) { - u, _ := ParseURL("http://x.com?foo=bar&bar=1&bar=2") - v := u.Query() - if len(v) != 2 { - t.Errorf("got %d keys in Query values, want 2", len(v)) - } - if g, e := v.Get("foo"), "bar"; g != e { - t.Errorf("Get(foo) = %q, want %q", g, e) - } - // Case sensitive: - if g, e := v.Get("Foo"), ""; g != e { - t.Errorf("Get(Foo) = %q, want %q", g, e) - } - if g, e := v.Get("bar"), "1"; g != e { - t.Errorf("Get(bar) = %q, want %q", g, e) - } - if g, e := v.Get("baz"), ""; g != e { - t.Errorf("Get(baz) = %q, want %q", g, e) - } - v.Del("bar") - if g, e := v.Get("bar"), ""; g != e { - t.Errorf("second Get(bar) = %q, want %q", g, e) - } -} diff --git a/src/pkg/json/decode.go b/src/pkg/json/decode.go index 7d474fa7b..4f6562bd5 100644 --- a/src/pkg/json/decode.go +++ b/src/pkg/json/decode.go @@ -251,6 +251,12 @@ func (d *decodeState) value(v reflect.Value) { // if it encounters an Unmarshaler, indirect stops and returns that. // if wantptr is true, indirect stops at the last pointer. func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, reflect.Value) { + // If v is a named type and is addressable, + // start with its address, so that if the type has pointer methods, + // we find them. + if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { + v = v.Addr() + } for { var isUnmarshaler bool if v.Type().NumMethod() > 0 { diff --git a/src/pkg/json/decode_test.go b/src/pkg/json/decode_test.go index c0ef5bc3a..a855d6048 100644 --- a/src/pkg/json/decode_test.go +++ b/src/pkg/json/decode_test.go @@ -34,10 +34,17 @@ func (u *unmarshaler) UnmarshalJSON(b []byte) os.Error { return nil } +type ustruct struct { + M unmarshaler +} + var ( um0, um1 unmarshaler // target2 of unmarshaling ump = &um1 umtrue = unmarshaler{true} + umslice = []unmarshaler{unmarshaler{true}} + umslicep = new([]unmarshaler) + umstruct = ustruct{unmarshaler{true}} ) type unmarshalTest struct { @@ -77,6 +84,9 @@ var unmarshalTests = []unmarshalTest{ // unmarshal interface test {`{"T":false}`, &um0, umtrue, nil}, // use "false" so test will fail if custom unmarshaler is not called {`{"T":false}`, &ump, &umtrue, nil}, + {`[{"T":false}]`, &umslice, umslice, nil}, + {`[{"T":false}]`, &umslicep, &umslice, nil}, + {`{"M":{"T":false}}`, &umstruct, umstruct, nil}, } func TestMarshal(t *testing.T) { @@ -140,7 +150,6 @@ func TestUnmarshal(t *testing.T) { println(string(data)) data, _ = Marshal(tt.out) println(string(data)) - return continue } } diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile index 6b3d0b328..eba9e26d9 100644 --- a/src/pkg/net/Makefile +++ b/src/pkg/net/Makefile @@ -9,16 +9,14 @@ GOFILES=\ dial.go\ dnsclient.go\ dnsmsg.go\ - fd_$(GOOS).go\ hosts.go\ interface.go\ ip.go\ - ipsock.go\ iprawsock.go\ + ipsock.go\ net.go\ parse.go\ pipe.go\ - sock.go\ tcpsock.go\ udpsock.go\ unixsock.go\ @@ -27,80 +25,129 @@ GOFILES_freebsd=\ dnsclient_unix.go\ dnsconfig.go\ fd.go\ + fd_$(GOOS).go\ file.go\ interface_bsd.go\ interface_freebsd.go\ + iprawsock_posix.go\ + ipsock_posix.go\ lookup_unix.go\ newpollserver.go\ port.go\ sendfile_stub.go\ + sock.go\ sock_bsd.go\ + tcpsock_posix.go\ + udpsock_posix.go\ + unixsock_posix.go\ +ifeq ($(CGO_ENABLED),1) CGOFILES_freebsd=\ cgo_bsd.go\ - cgo_unix.go\ + cgo_unix.go +else +GOFILES_freebsd+=cgo_stub.go +endif GOFILES_darwin=\ dnsclient_unix.go\ dnsconfig.go\ fd.go\ + fd_$(GOOS).go\ file.go\ interface_bsd.go\ interface_darwin.go\ + iprawsock_posix.go\ + ipsock_posix.go\ lookup_unix.go\ newpollserver.go\ port.go\ sendfile_stub.go\ + sock.go\ sock_bsd.go\ + tcpsock_posix.go\ + udpsock_posix.go\ + unixsock_posix.go\ +ifeq ($(CGO_ENABLED),1) CGOFILES_darwin=\ cgo_bsd.go\ - cgo_unix.go\ + cgo_unix.go +else +GOFILES_darwin+=cgo_stub.go +endif GOFILES_linux=\ dnsclient_unix.go\ dnsconfig.go\ fd.go\ + fd_$(GOOS).go\ file.go\ interface_linux.go\ + iprawsock_posix.go\ + ipsock_posix.go\ lookup_unix.go\ newpollserver.go\ port.go\ sendfile_linux.go\ + sock.go\ sock_linux.go\ + tcpsock_posix.go\ + udpsock_posix.go\ + unixsock_posix.go\ + +ifeq ($(CGO_ENABLED),1) +CGOFILES_linux=\ + cgo_linux.go\ + cgo_unix.go +else +GOFILES_linux+=cgo_stub.go +endif GOFILES_openbsd=\ - dnsclient.go\ + dnsclient_unix.go\ dnsconfig.go\ fd.go\ + fd_$(GOOS).go\ file.go\ interface_bsd.go\ + interface_openbsd.go\ + iprawsock_posix.go\ + ipsock_posix.go\ + lookup_unix.go\ newpollserver.go\ port.go\ sendfile_stub.go\ + sock.go\ sock_bsd.go\ + tcpsock_posix.go\ + udpsock_posix.go\ + unixsock_posix.go\ cgo_stub.go\ GOFILES_plan9=\ + file_plan9.go\ interface_stub.go\ - lookup_unix.go\ - sendfile_stub.go\ - -ifeq ($(GOARCH),arm) -# ARM has no cgo, so use the stubs. -GOFILES_linux+=cgo_stub.go -else -CGOFILES_linux=\ - cgo_linux.go\ - cgo_unix.go -endif + iprawsock_plan9.go\ + ipsock_plan9.go\ + lookup_plan9.go\ + tcpsock_plan9.go\ + udpsock_plan9.go\ + unixsock_plan9.go\ GOFILES_windows=\ + fd_$(GOOS).go\ file_windows.go\ interface_windows.go\ + iprawsock_posix.go\ + ipsock_posix.go\ lookup_windows.go\ sendfile_windows.go\ + sock.go\ sock_windows.go\ + tcpsock_posix.go\ + udpsock_posix.go\ + unixsock_posix.go\ GOFILES+=$(GOFILES_$(GOOS)) ifneq ($(CGOFILES_$(GOOS)),) diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go index cd1a21dc3..707dccaa4 100644 --- a/src/pkg/net/fd.go +++ b/src/pkg/net/fd.go @@ -585,20 +585,25 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. fd.incref() defer fd.decref() + if fd.rdeadline_delta > 0 { + fd.rdeadline = pollserver.Now() + fd.rdeadline_delta + } else { + fd.rdeadline = 0 + } // See ../syscall/exec.go for description of ForkLock. // It is okay to hold the lock across syscall.Accept // because we have put fd.sysfd into non-blocking mode. syscall.ForkLock.RLock() var s, e int - var sa syscall.Sockaddr + var rsa syscall.Sockaddr for { if fd.closing { syscall.ForkLock.RUnlock() return nil, os.EINVAL } - s, sa, e = syscall.Accept(fd.sysfd) - if e != syscall.EAGAIN { + s, rsa, e = syscall.Accept(fd.sysfd) + if e != syscall.EAGAIN || fd.rdeadline < 0 { break } syscall.ForkLock.RUnlock() @@ -616,7 +621,8 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. syscall.Close(s) return nil, err } - nfd.setAddr(fd.laddr, toAddr(sa)) + lsa, _ := syscall.Getsockname(nfd.sysfd) + nfd.setAddr(toAddr(lsa), toAddr(rsa)) return nfd, nil } diff --git a/src/pkg/net/file_plan9.go b/src/pkg/net/file_plan9.go new file mode 100644 index 000000000..a07e74331 --- /dev/null +++ b/src/pkg/net/file_plan9.go @@ -0,0 +1,33 @@ +// 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 net + +import ( + "os" +) + +// FileConn returns a copy of the network connection corresponding to +// the open file f. It is the caller's responsibility to close f when +// finished. Closing c does not affect f, and closing f does not +// affect c. +func FileConn(f *os.File) (c Conn, err os.Error) { + return nil, os.EPLAN9 +} + +// FileListener returns a copy of the network listener corresponding +// to the open file f. It is the caller's responsibility to close l +// when finished. Closing c does not affect l, and closing l does not +// affect c. +func FileListener(f *os.File) (l Listener, err os.Error) { + return nil, os.EPLAN9 +} + +// FilePacketConn returns a copy of the packet network connection +// corresponding to the open file f. It is the caller's +// responsibility to close f when finished. Closing c does not affect +// f, and closing f does not affect c. +func FilePacketConn(f *os.File) (c PacketConn, err os.Error) { + return nil, os.EPLAN9 +} diff --git a/src/pkg/net/file_test.go b/src/pkg/net/file_test.go index bd1e2c9d7..9a8c2dcbc 100644 --- a/src/pkg/net/file_test.go +++ b/src/pkg/net/file_test.go @@ -57,7 +57,7 @@ func testFileListener(t *testing.T, net, laddr string) { } func TestFileListener(t *testing.T) { - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } testFileListener(t, "tcp", "127.0.0.1") @@ -116,7 +116,7 @@ func testFilePacketConnDial(t *testing.T, net, raddr string) { } func TestFilePacketConn(t *testing.T) { - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } testFilePacketConnListen(t, "udp", "127.0.0.1:0") diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go index 130820d4a..2675f94b9 100644 --- a/src/pkg/net/interface_bsd.go +++ b/src/pkg/net/interface_bsd.go @@ -169,34 +169,3 @@ func newAddr(m *syscall.InterfaceAddrMessage) ([]Addr, os.Error) { return ifat, nil } - -func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, os.Error) { - var ifmat []Addr - - sas, e := syscall.ParseRoutingSockaddr(m) - if e != 0 { - return nil, os.NewSyscallError("route sockaddr", e) - } - - for _, s := range sas { - switch v := s.(type) { - case *syscall.SockaddrInet4: - ifma := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])} - ifmat = append(ifmat, ifma.toAddr()) - case *syscall.SockaddrInet6: - ifma := &IPAddr{IP: make(IP, IPv6len)} - copy(ifma.IP, v.Addr[:]) - // NOTE: KAME based IPv6 protcol stack usually embeds - // the interface index in the interface-local or link- - // local address as the kernel-internal form. - if ifma.IP.IsInterfaceLocalMulticast() || - ifma.IP.IsLinkLocalMulticast() { - // remove embedded scope zone ID - ifma.IP[2], ifma.IP[3] = 0, 0 - } - ifmat = append(ifmat, ifma.toAddr()) - } - } - - return ifmat, nil -} diff --git a/src/pkg/net/interface_darwin.go b/src/pkg/net/interface_darwin.go index 6fbcd3723..a7b68ad7f 100644 --- a/src/pkg/net/interface_darwin.go +++ b/src/pkg/net/interface_darwin.go @@ -47,3 +47,34 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, os.Error) { return ifmat, nil } + +func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, os.Error) { + var ifmat []Addr + + sas, e := syscall.ParseRoutingSockaddr(m) + if e != 0 { + return nil, os.NewSyscallError("route sockaddr", e) + } + + for _, s := range sas { + switch v := s.(type) { + case *syscall.SockaddrInet4: + ifma := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])} + ifmat = append(ifmat, ifma.toAddr()) + case *syscall.SockaddrInet6: + ifma := &IPAddr{IP: make(IP, IPv6len)} + copy(ifma.IP, v.Addr[:]) + // NOTE: KAME based IPv6 protcol stack usually embeds + // the interface index in the interface-local or link- + // local address as the kernel-internal form. + if ifma.IP.IsInterfaceLocalMulticast() || + ifma.IP.IsLinkLocalMulticast() { + // remove embedded scope zone ID + ifma.IP[2], ifma.IP[3] = 0, 0 + } + ifmat = append(ifmat, ifma.toAddr()) + } + } + + return ifmat, nil +} diff --git a/src/pkg/net/interface_freebsd.go b/src/pkg/net/interface_freebsd.go index e0ff6caf0..20f506b08 100644 --- a/src/pkg/net/interface_freebsd.go +++ b/src/pkg/net/interface_freebsd.go @@ -47,3 +47,34 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, os.Error) { return ifmat, nil } + +func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, os.Error) { + var ifmat []Addr + + sas, e := syscall.ParseRoutingSockaddr(m) + if e != 0 { + return nil, os.NewSyscallError("route sockaddr", e) + } + + for _, s := range sas { + switch v := s.(type) { + case *syscall.SockaddrInet4: + ifma := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])} + ifmat = append(ifmat, ifma.toAddr()) + case *syscall.SockaddrInet6: + ifma := &IPAddr{IP: make(IP, IPv6len)} + copy(ifma.IP, v.Addr[:]) + // NOTE: KAME based IPv6 protcol stack usually embeds + // the interface index in the interface-local or link- + // local address as the kernel-internal form. + if ifma.IP.IsInterfaceLocalMulticast() || + ifma.IP.IsLinkLocalMulticast() { + // remove embedded scope zone ID + ifma.IP[2], ifma.IP[3] = 0, 0 + } + ifmat = append(ifmat, ifma.toAddr()) + } + } + + return ifmat, nil +} diff --git a/src/pkg/net/interface_openbsd.go b/src/pkg/net/interface_openbsd.go new file mode 100644 index 000000000..f18149393 --- /dev/null +++ b/src/pkg/net/interface_openbsd.go @@ -0,0 +1,16 @@ +// 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. + +// Network interface identification for OpenBSD + +package net + +import "os" + +// If the ifindex is zero, interfaceMulticastAddrTable returns +// addresses for all network interfaces. Otherwise it returns +// addresses for a specific interface. +func interfaceMulticastAddrTable(ifindex int) ([]Addr, os.Error) { + return nil, nil +} diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go index 43047a78e..662b9f57b 100644 --- a/src/pkg/net/iprawsock.go +++ b/src/pkg/net/iprawsock.go @@ -8,22 +8,8 @@ package net import ( "os" - "sync" - "syscall" ) -var onceReadProtocols sync.Once - -func sockaddrToIP(sa syscall.Sockaddr) Addr { - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - return &IPAddr{sa.Addr[0:]} - case *syscall.SockaddrInet6: - return &IPAddr{sa.Addr[0:]} - } - return nil -} - // IPAddr represents the address of a IP end point. type IPAddr struct { IP IP @@ -39,27 +25,6 @@ func (a *IPAddr) String() string { return a.IP.String() } -func (a *IPAddr) family() int { - if a == nil || len(a.IP) <= 4 { - return syscall.AF_INET - } - if a.IP.To4() != nil { - return syscall.AF_INET - } - return syscall.AF_INET6 -} - -func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { - return ipToSockaddr(family, a.IP, 0) -} - -func (a *IPAddr) toAddr() sockaddr { - if a == nil { // nil *IPAddr - return nil // nil interface - } - return a -} - // ResolveIPAddr parses addr as a IP address and resolves domain // names to numeric addresses on the network net, which must be // "ip", "ip4" or "ip6". A literal IPv6 host address must be @@ -72,168 +37,6 @@ func ResolveIPAddr(net, addr string) (*IPAddr, os.Error) { return &IPAddr{ip}, nil } -// IPConn is the implementation of the Conn and PacketConn -// interfaces for IP network connections. -type IPConn struct { - fd *netFD -} - -func newIPConn(fd *netFD) *IPConn { return &IPConn{fd} } - -func (c *IPConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the net.Conn Read method. -func (c *IPConn) Read(b []byte) (n int, err os.Error) { - n, _, err = c.ReadFrom(b) - return -} - -// Write implements the net.Conn Write method. -func (c *IPConn) Write(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the IP connection. -func (c *IPConn) Close() os.Error { - if !c.ok() { - return os.EINVAL - } - err := c.fd.Close() - c.fd = nil - return err -} - -// LocalAddr returns the local network address. -func (c *IPConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *IPAddr. -func (c *IPConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetTimeout implements the net.Conn SetTimeout method. -func (c *IPConn) SetTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setTimeout(c.fd, nsec) -} - -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *IPConn) SetReadTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadTimeout(c.fd, nsec) -} - -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *IPConn) SetWriteTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteTimeout(c.fd, nsec) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *IPConn) SetReadBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *IPConn) SetWriteBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - -// IP-specific methods. - -// ReadFromIP reads a IP packet from c, copying the payload into b. -// It returns the number of bytes copied into b and the return address -// that was on the packet. -// -// ReadFromIP can be made to time out and return an error with -// Timeout() == true after a fixed time limit; see SetTimeout and -// SetReadTimeout. -func (c *IPConn) ReadFromIP(b []byte) (n int, addr *IPAddr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - // TODO(cw,rsc): consider using readv if we know the family - // type to avoid the header trim/copy - n, sa, err := c.fd.ReadFrom(b) - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - addr = &IPAddr{sa.Addr[0:]} - if len(b) >= 4 { // discard ipv4 header - hsize := (int(b[0]) & 0xf) * 4 - copy(b, b[hsize:]) - n -= hsize - } - case *syscall.SockaddrInet6: - addr = &IPAddr{sa.Addr[0:]} - } - return -} - -// ReadFrom implements the net.PacketConn ReadFrom method. -func (c *IPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - n, uaddr, err := c.ReadFromIP(b) - return n, uaddr.toAddr(), err -} - -// WriteToIP writes a IP packet to addr via c, copying the payload from b. -// -// WriteToIP can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. -// On packet-oriented connections, write timeouts are rare. -func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - sa, err1 := addr.sockaddr(c.fd.family) - if err1 != nil { - return 0, &OpError{Op: "write", Net: "ip", Addr: addr, Error: err1} - } - return c.fd.WriteTo(b, sa) -} - -// WriteTo implements the net.PacketConn WriteTo method. -func (c *IPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - a, ok := addr.(*IPAddr) - if !ok { - return 0, &OpError{"writeto", "ip", addr, os.EINVAL} - } - return c.WriteToIP(b, a) -} - // Convert "host" into IP address. func hostToIP(net, host string) (ip IP, err os.Error) { var addr IP @@ -264,100 +67,3 @@ func hostToIP(net, host string) (ip IP, err os.Error) { Error: return nil, err } - -var protocols map[string]int - -func readProtocols() { - protocols = make(map[string]int) - if file, err := open("/etc/protocols"); err == nil { - for line, ok := file.readLine(); ok; line, ok = file.readLine() { - // tcp 6 TCP # transmission control protocol - if i := byteIndex(line, '#'); i >= 0 { - line = line[0:i] - } - f := getFields(line) - if len(f) < 2 { - continue - } - if proto, _, ok := dtoi(f[1], 0); ok { - protocols[f[0]] = proto - for _, alias := range f[2:] { - protocols[alias] = proto - } - } - } - file.close() - } -} - -func splitNetProto(netProto string) (net string, proto int, err os.Error) { - onceReadProtocols.Do(readProtocols) - i := last(netProto, ':') - if i < 0 { // no colon - return "", 0, os.NewError("no IP protocol specified") - } - net = netProto[0:i] - protostr := netProto[i+1:] - proto, i, ok := dtoi(protostr, 0) - if !ok || i != len(protostr) { - // lookup by name - proto, ok = protocols[protostr] - if ok { - return - } - } - return -} - -// DialIP connects to the remote address raddr on the network net, -// which must be "ip", "ip4", or "ip6". -func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) { - net, proto, err := splitNetProto(netProto) - if err != nil { - return - } - switch net { - case "ip", "ip4", "ip6": - default: - return nil, UnknownNetworkError(net) - } - if raddr == nil { - return nil, &OpError{"dial", "ip", nil, errMissingAddress} - } - fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_RAW, proto, "dial", sockaddrToIP) - if e != nil { - return nil, e - } - return newIPConn(fd), nil -} - -// ListenIP listens for incoming IP packets addressed to the -// local address laddr. The returned connection c's ReadFrom -// and WriteTo methods can be used to receive and send IP -// packets with per-packet addressing. -func ListenIP(netProto string, laddr *IPAddr) (c *IPConn, err os.Error) { - net, proto, err := splitNetProto(netProto) - if err != nil { - return - } - switch net { - case "ip", "ip4", "ip6": - default: - return nil, UnknownNetworkError(net) - } - fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_RAW, proto, "dial", sockaddrToIP) - if e != nil { - return nil, e - } - return newIPConn(fd), nil -} - -// BindToDevice binds an IPConn to a network interface. -func (c *IPConn) BindToDevice(device string) os.Error { - if !c.ok() { - return os.EINVAL - } - c.fd.incref() - defer c.fd.decref() - return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device)) -} diff --git a/src/pkg/net/iprawsock_plan9.go b/src/pkg/net/iprawsock_plan9.go new file mode 100644 index 000000000..808e17974 --- /dev/null +++ b/src/pkg/net/iprawsock_plan9.go @@ -0,0 +1,99 @@ +// 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. + +// (Raw) IP sockets stubs for Plan 9 + +package net + +import ( + "os" +) + +// IPConn is the implementation of the Conn and PacketConn +// interfaces for IP network connections. +type IPConn bool + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *IPConn) Read(b []byte) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +// Write implements the net.Conn Write method. +func (c *IPConn) Write(b []byte) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +// Close closes the IP connection. +func (c *IPConn) Close() os.Error { + return os.EPLAN9 +} + +// LocalAddr returns the local network address. +func (c *IPConn) LocalAddr() Addr { + return nil +} + +// RemoteAddr returns the remote network address, a *IPAddr. +func (c *IPConn) RemoteAddr() Addr { + return nil +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *IPConn) SetTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *IPConn) SetReadTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *IPConn) SetWriteTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// IP-specific methods. + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *IPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + err = os.EPLAN9 + return +} + +// WriteToIP writes a IP packet to addr via c, copying the payload from b. +// +// WriteToIP can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetWriteTimeout. +// On packet-oriented connections, write timeouts are rare. +func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *IPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +func splitNetProto(netProto string) (net string, proto int, err os.Error) { + err = os.EPLAN9 + return +} + +// DialIP connects to the remote address raddr on the network net, +// which must be "ip", "ip4", or "ip6". +func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) { + return nil, os.EPLAN9 +} + +// ListenIP listens for incoming IP packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send IP +// packets with per-packet addressing. +func ListenIP(netProto string, laddr *IPAddr) (c *IPConn, err os.Error) { + return nil, os.EPLAN9 +} diff --git a/src/pkg/net/iprawsock_posix.go b/src/pkg/net/iprawsock_posix.go new file mode 100644 index 000000000..4e1151800 --- /dev/null +++ b/src/pkg/net/iprawsock_posix.go @@ -0,0 +1,305 @@ +// 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. + +// (Raw) IP sockets + +package net + +import ( + "os" + "sync" + "syscall" +) + +var onceReadProtocols sync.Once + +func sockaddrToIP(sa syscall.Sockaddr) Addr { + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + return &IPAddr{sa.Addr[0:]} + case *syscall.SockaddrInet6: + return &IPAddr{sa.Addr[0:]} + } + return nil +} + +func (a *IPAddr) family() int { + if a == nil || len(a.IP) <= 4 { + return syscall.AF_INET + } + if a.IP.To4() != nil { + return syscall.AF_INET + } + return syscall.AF_INET6 +} + +func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { + return ipToSockaddr(family, a.IP, 0) +} + +func (a *IPAddr) toAddr() sockaddr { + if a == nil { // nil *IPAddr + return nil // nil interface + } + return a +} + +// IPConn is the implementation of the Conn and PacketConn +// interfaces for IP network connections. +type IPConn struct { + fd *netFD +} + +func newIPConn(fd *netFD) *IPConn { return &IPConn{fd} } + +func (c *IPConn) ok() bool { return c != nil && c.fd != nil } + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *IPConn) Read(b []byte) (n int, err os.Error) { + n, _, err = c.ReadFrom(b) + return +} + +// Write implements the net.Conn Write method. +func (c *IPConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b) +} + +// Close closes the IP connection. +func (c *IPConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close() + c.fd = nil + return err +} + +// LocalAddr returns the local network address. +func (c *IPConn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.laddr +} + +// RemoteAddr returns the remote network address, a *IPAddr. +func (c *IPConn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.raddr +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *IPConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec) +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *IPConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec) +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *IPConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec) +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *IPConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes) +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *IPConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes) +} + +// IP-specific methods. + +// ReadFromIP reads a IP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFromIP can be made to time out and return an error with +// Timeout() == true after a fixed time limit; see SetTimeout and +// SetReadTimeout. +func (c *IPConn) ReadFromIP(b []byte) (n int, addr *IPAddr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + // TODO(cw,rsc): consider using readv if we know the family + // type to avoid the header trim/copy + n, sa, err := c.fd.ReadFrom(b) + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + addr = &IPAddr{sa.Addr[0:]} + if len(b) >= 4 { // discard ipv4 header + hsize := (int(b[0]) & 0xf) * 4 + copy(b, b[hsize:]) + n -= hsize + } + case *syscall.SockaddrInet6: + addr = &IPAddr{sa.Addr[0:]} + } + return +} + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *IPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, uaddr, err := c.ReadFromIP(b) + return n, uaddr.toAddr(), err +} + +// WriteToIP writes a IP packet to addr via c, copying the payload from b. +// +// WriteToIP can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetWriteTimeout. +// On packet-oriented connections, write timeouts are rare. +func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + sa, err1 := addr.sockaddr(c.fd.family) + if err1 != nil { + return 0, &OpError{Op: "write", Net: "ip", Addr: addr, Error: err1} + } + return c.fd.WriteTo(b, sa) +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *IPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + a, ok := addr.(*IPAddr) + if !ok { + return 0, &OpError{"writeto", "ip", addr, os.EINVAL} + } + return c.WriteToIP(b, a) +} + +var protocols map[string]int + +func readProtocols() { + protocols = make(map[string]int) + if file, err := open("/etc/protocols"); err == nil { + for line, ok := file.readLine(); ok; line, ok = file.readLine() { + // tcp 6 TCP # transmission control protocol + if i := byteIndex(line, '#'); i >= 0 { + line = line[0:i] + } + f := getFields(line) + if len(f) < 2 { + continue + } + if proto, _, ok := dtoi(f[1], 0); ok { + protocols[f[0]] = proto + for _, alias := range f[2:] { + protocols[alias] = proto + } + } + } + file.close() + } +} + +func splitNetProto(netProto string) (net string, proto int, err os.Error) { + onceReadProtocols.Do(readProtocols) + i := last(netProto, ':') + if i < 0 { // no colon + return "", 0, os.NewError("no IP protocol specified") + } + net = netProto[0:i] + protostr := netProto[i+1:] + proto, i, ok := dtoi(protostr, 0) + if !ok || i != len(protostr) { + // lookup by name + proto, ok = protocols[protostr] + if ok { + return + } + } + return +} + +// DialIP connects to the remote address raddr on the network net, +// which must be "ip", "ip4", or "ip6". +func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) { + net, proto, err := splitNetProto(netProto) + if err != nil { + return + } + switch net { + case "ip", "ip4", "ip6": + default: + return nil, UnknownNetworkError(net) + } + if raddr == nil { + return nil, &OpError{"dial", "ip", nil, errMissingAddress} + } + fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_RAW, proto, "dial", sockaddrToIP) + if e != nil { + return nil, e + } + return newIPConn(fd), nil +} + +// ListenIP listens for incoming IP packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send IP +// packets with per-packet addressing. +func ListenIP(netProto string, laddr *IPAddr) (c *IPConn, err os.Error) { + net, proto, err := splitNetProto(netProto) + if err != nil { + return + } + switch net { + case "ip", "ip4", "ip6": + default: + return nil, UnknownNetworkError(net) + } + fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_RAW, proto, "dial", sockaddrToIP) + if e != nil { + return nil, e + } + return newIPConn(fd), nil +} + +// BindToDevice binds an IPConn to a network interface. +func (c *IPConn) BindToDevice(device string) os.Error { + if !c.ok() { + return os.EINVAL + } + c.fd.incref() + defer c.fd.decref() + return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device)) +} diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go index e831d9afc..4e2a5622b 100644 --- a/src/pkg/net/ipsock.go +++ b/src/pkg/net/ipsock.go @@ -8,94 +8,10 @@ package net import ( "os" - "syscall" ) -// Should we try to use the IPv4 socket interface if we're -// only dealing with IPv4 sockets? As long as the host system -// understands IPv6, it's okay to pass IPv4 addresses to the IPv6 -// interface. That simplifies our code and is most general. -// Unfortunately, we need to run on kernels built without IPv6 -// support too. So probe the kernel to figure it out. -// -// probeIPv6Stack probes both basic IPv6 capability and IPv6 IPv4- -// mapping capability which is controlled by IPV6_V6ONLY socket -// option and/or kernel state "net.inet6.ip6.v6only". -// It returns two boolean values. If the first boolean value is -// true, kernel supports basic IPv6 functionality. If the second -// boolean value is true, kernel supports IPv6 IPv4-mapping. -func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { - var probes = []struct { - la TCPAddr - ok bool - }{ - // IPv6 communication capability - {TCPAddr{IP: ParseIP("::1")}, false}, - // IPv6 IPv4-mapped address communication capability - {TCPAddr{IP: IPv4(127, 0, 0, 1)}, false}, - } - - for i := range probes { - s, errno := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) - if errno != 0 { - continue - } - defer closesocket(s) - sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6) - if err != nil { - continue - } - errno = syscall.Bind(s, sa) - if errno != 0 { - continue - } - probes[i].ok = true - } - - return probes[0].ok, probes[1].ok -} - var supportsIPv6, supportsIPv4map = probeIPv6Stack() -// favoriteAddrFamily returns the appropriate address family to -// the given net, raddr, laddr and mode. At first it figures -// address family out from the net. If mode indicates "listen" -// and laddr.(type).IP is nil, it assumes that the user wants to -// make a passive connection with wildcard address family, both -// INET and INET6, and wildcard address. Otherwise guess: if the -// addresses are IPv4 then returns INET, or else returns INET6. -func favoriteAddrFamily(net string, raddr, laddr sockaddr, mode string) int { - switch net[len(net)-1] { - case '4': - return syscall.AF_INET - case '6': - return syscall.AF_INET6 - } - - if mode == "listen" { - switch a := laddr.(type) { - case *TCPAddr: - if a.IP == nil && supportsIPv6 { - return syscall.AF_INET6 - } - case *UDPAddr: - if a.IP == nil && supportsIPv6 { - return syscall.AF_INET6 - } - case *IPAddr: - if a.IP == nil && supportsIPv6 { - return syscall.AF_INET6 - } - } - } - - if (laddr == nil || laddr.family() == syscall.AF_INET) && - (raddr == nil || raddr.family() == syscall.AF_INET) { - return syscall.AF_INET - } - return syscall.AF_INET6 -} - func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) { if filter == anyaddr { // We'll take any IP address, but since the dialing code @@ -143,93 +59,12 @@ func ipv6only(x IP) IP { return nil } -// TODO(rsc): if syscall.OS == "linux", we're supposed to read -// /proc/sys/net/core/somaxconn, -// to take advantage of kernels that have raised the limit. -func listenBacklog() int { return syscall.SOMAXCONN } - -// Internet sockets (TCP, UDP) - -// A sockaddr represents a TCP or UDP network address that can -// be converted into a syscall.Sockaddr. -type sockaddr interface { - Addr - sockaddr(family int) (syscall.Sockaddr, os.Error) - family() int -} - -func internetSocket(net string, laddr, raddr sockaddr, socktype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) { - var oserr os.Error - var la, ra syscall.Sockaddr - family := favoriteAddrFamily(net, raddr, laddr, mode) - if laddr != nil { - if la, oserr = laddr.sockaddr(family); oserr != nil { - goto Error - } - } - if raddr != nil { - if ra, oserr = raddr.sockaddr(family); oserr != nil { - goto Error - } - } - fd, oserr = socket(net, family, socktype, proto, la, ra, toAddr) - if oserr != nil { - goto Error - } - return fd, nil - -Error: - addr := raddr - if mode == "listen" { - addr = laddr - } - return nil, &OpError{mode, net, addr, oserr} -} - type InvalidAddrError string func (e InvalidAddrError) String() string { return string(e) } func (e InvalidAddrError) Timeout() bool { return false } func (e InvalidAddrError) Temporary() bool { return false } -func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { - switch family { - case syscall.AF_INET: - if len(ip) == 0 { - ip = IPv4zero - } - if ip = ip.To4(); ip == nil { - return nil, InvalidAddrError("non-IPv4 address") - } - s := new(syscall.SockaddrInet4) - for i := 0; i < IPv4len; i++ { - s.Addr[i] = ip[i] - } - s.Port = port - return s, nil - case syscall.AF_INET6: - if len(ip) == 0 { - ip = IPv6zero - } - // IPv4 callers use 0.0.0.0 to mean "announce on any available address". - // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0", - // which it refuses to do. Rewrite to the IPv6 all zeros. - if ip.Equal(IPv4zero) { - ip = IPv6zero - } - if ip = ip.To16(); ip == nil { - return nil, InvalidAddrError("non-IPv6 address") - } - s := new(syscall.SockaddrInet6) - for i := 0; i < IPv6len; i++ { - s.Addr[i] = ip[i] - } - s.Port = port - return s, nil - } - return nil, InvalidAddrError("unexpected socket family") -} - // SplitHostPort splits a network address of the form // "host:port" or "[host]:port" into host and port. // The latter form must be used when host contains a colon. diff --git a/src/pkg/net/ipsock_plan9.go b/src/pkg/net/ipsock_plan9.go new file mode 100644 index 000000000..9e5da6d38 --- /dev/null +++ b/src/pkg/net/ipsock_plan9.go @@ -0,0 +1,305 @@ +// 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. + +// IP sockets stubs for Plan 9 + +package net + +import ( + "os" +) + +// probeIPv6Stack returns two boolean values. If the first boolean value is +// true, kernel supports basic IPv6 functionality. If the second +// boolean value is true, kernel supports IPv6 IPv4-mapping. +func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { + return false, false +} + +// parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80). +func parsePlan9Addr(s string) (ip IP, iport int, err os.Error) { + var ( + addr IP + p, i int + ok bool + ) + addr = IPv4zero // address contains port only + i = byteIndex(s, '!') + if i >= 0 { + addr = ParseIP(s[:i]) + if addr == nil { + err = os.NewError("net: parsing IP failed") + goto Error + } + } + p, _, ok = dtoi(s[i+1:], 0) + if !ok { + err = os.NewError("net: parsing port failed") + goto Error + } + if p < 0 || p > 0xFFFF { + err = &AddrError{"invalid port", string(p)} + goto Error + } + return addr, p, nil + +Error: + return nil, 0, err +} + +func readPlan9Addr(proto, filename string) (addr Addr, err os.Error) { + var buf [128]byte + + f, err := os.Open(filename) + if err != nil { + return + } + n, err := f.Read(buf[:]) + if err != nil { + return + } + ip, port, err := parsePlan9Addr(string(buf[:n])) + if err != nil { + return + } + switch proto { + case "tcp": + addr = &TCPAddr{ip, port} + case "udp": + addr = &UDPAddr{ip, port} + default: + return nil, os.NewError("unknown protocol " + proto) + } + return addr, nil +} + +type plan9Conn struct { + proto, name, dir string + ctl, data *os.File + laddr, raddr Addr +} + +func newPlan9Conn(proto, name string, ctl *os.File, laddr, raddr Addr) *plan9Conn { + return &plan9Conn{proto, name, "/net/" + proto + "/" + name, ctl, nil, laddr, raddr} +} + +func (c *plan9Conn) ok() bool { return c != nil && c.ctl != nil } + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *plan9Conn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + if c.data == nil { + c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) + if err != nil { + return 0, err + } + } + n, err = c.data.Read(b) + if c.proto == "udp" && err == os.EOF { + n = 0 + err = nil + } + return +} + +// Write implements the net.Conn Write method. +func (c *plan9Conn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + if c.data == nil { + c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) + if err != nil { + return 0, err + } + } + return c.data.Write(b) +} + +// Close closes the connection. +func (c *plan9Conn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.ctl.Close() + if err != nil { + return err + } + if c.data != nil { + err = c.data.Close() + } + c.ctl = nil + c.data = nil + return err +} + +// LocalAddr returns the local network address. +func (c *plan9Conn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.laddr +} + +// RemoteAddr returns the remote network address. +func (c *plan9Conn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.raddr +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *plan9Conn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return os.EPLAN9 +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *plan9Conn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return os.EPLAN9 +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *plan9Conn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return os.EPLAN9 +} + +func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string, err os.Error) { + var ( + ip IP + port int + ) + switch a := addr.(type) { + case *TCPAddr: + proto = "tcp" + ip = a.IP + port = a.Port + case *UDPAddr: + proto = "udp" + ip = a.IP + port = a.Port + default: + err = UnknownNetworkError(net) + return + } + + clone, dest, err := queryCS1(proto, ip, port) + if err != nil { + return + } + f, err := os.OpenFile(clone, os.O_RDWR, 0) + if err != nil { + return + } + var buf [16]byte + n, err := f.Read(buf[:]) + if err != nil { + return + } + return f, dest, proto, string(buf[:n]), nil +} + +func dialPlan9(net string, laddr, raddr Addr) (c *plan9Conn, err os.Error) { + f, dest, proto, name, err := startPlan9(net, raddr) + if err != nil { + return + } + _, err = f.WriteString("connect " + dest) + if err != nil { + return + } + laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local") + if err != nil { + return + } + raddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/remote") + if err != nil { + return + } + return newPlan9Conn(proto, name, f, laddr, raddr), nil +} + +type plan9Listener struct { + proto, name, dir string + ctl *os.File + laddr Addr +} + +func listenPlan9(net string, laddr Addr) (l *plan9Listener, err os.Error) { + f, dest, proto, name, err := startPlan9(net, laddr) + if err != nil { + return + } + _, err = f.WriteString("announce " + dest) + if err != nil { + return + } + laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local") + if err != nil { + return + } + l = new(plan9Listener) + l.proto = proto + l.name = name + l.dir = "/net/" + proto + "/" + name + l.ctl = f + l.laddr = laddr + return l, nil +} + +func (l *plan9Listener) plan9Conn() *plan9Conn { + return newPlan9Conn(l.proto, l.name, l.ctl, l.laddr, nil) +} + +func (l *plan9Listener) acceptPlan9() (c *plan9Conn, err os.Error) { + f, err := os.Open(l.dir + "/listen") + if err != nil { + return + } + var buf [16]byte + n, err := f.Read(buf[:]) + if err != nil { + return + } + name := string(buf[:n]) + laddr, err := readPlan9Addr(l.proto, l.dir+"/local") + if err != nil { + return + } + raddr, err := readPlan9Addr(l.proto, l.dir+"/remote") + if err != nil { + return + } + return newPlan9Conn(l.proto, name, f, laddr, raddr), nil +} + +func (l *plan9Listener) Accept() (c Conn, err os.Error) { + c1, err := l.acceptPlan9() + if err != nil { + return + } + return c1, nil +} + +func (l *plan9Listener) Close() os.Error { + if l == nil || l.ctl == nil { + return os.EINVAL + } + return l.ctl.Close() +} + +func (l *plan9Listener) Addr() Addr { return l.laddr } diff --git a/src/pkg/net/ipsock_posix.go b/src/pkg/net/ipsock_posix.go new file mode 100644 index 000000000..0c522fb7f --- /dev/null +++ b/src/pkg/net/ipsock_posix.go @@ -0,0 +1,174 @@ +// 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 net + +import ( + "os" + "syscall" +) + +// Should we try to use the IPv4 socket interface if we're +// only dealing with IPv4 sockets? As long as the host system +// understands IPv6, it's okay to pass IPv4 addresses to the IPv6 +// interface. That simplifies our code and is most general. +// Unfortunately, we need to run on kernels built without IPv6 +// support too. So probe the kernel to figure it out. +// +// probeIPv6Stack probes both basic IPv6 capability and IPv6 IPv4- +// mapping capability which is controlled by IPV6_V6ONLY socket +// option and/or kernel state "net.inet6.ip6.v6only". +// It returns two boolean values. If the first boolean value is +// true, kernel supports basic IPv6 functionality. If the second +// boolean value is true, kernel supports IPv6 IPv4-mapping. +func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { + var probes = []struct { + la TCPAddr + ok bool + }{ + // IPv6 communication capability + {TCPAddr{IP: ParseIP("::1")}, false}, + // IPv6 IPv4-mapped address communication capability + {TCPAddr{IP: IPv4(127, 0, 0, 1)}, false}, + } + + for i := range probes { + s, errno := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + if errno != 0 { + continue + } + defer closesocket(s) + sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6) + if err != nil { + continue + } + errno = syscall.Bind(s, sa) + if errno != 0 { + continue + } + probes[i].ok = true + } + + return probes[0].ok, probes[1].ok +} + +// favoriteAddrFamily returns the appropriate address family to +// the given net, raddr, laddr and mode. At first it figures +// address family out from the net. If mode indicates "listen" +// and laddr.(type).IP is nil, it assumes that the user wants to +// make a passive connection with wildcard address family, both +// INET and INET6, and wildcard address. Otherwise guess: if the +// addresses are IPv4 then returns INET, or else returns INET6. +func favoriteAddrFamily(net string, raddr, laddr sockaddr, mode string) int { + switch net[len(net)-1] { + case '4': + return syscall.AF_INET + case '6': + return syscall.AF_INET6 + } + + if mode == "listen" { + switch a := laddr.(type) { + case *TCPAddr: + if a.IP == nil && supportsIPv6 { + return syscall.AF_INET6 + } + case *UDPAddr: + if a.IP == nil && supportsIPv6 { + return syscall.AF_INET6 + } + case *IPAddr: + if a.IP == nil && supportsIPv6 { + return syscall.AF_INET6 + } + } + } + + if (laddr == nil || laddr.family() == syscall.AF_INET) && + (raddr == nil || raddr.family() == syscall.AF_INET) { + return syscall.AF_INET + } + return syscall.AF_INET6 +} + +// TODO(rsc): if syscall.OS == "linux", we're supposed to read +// /proc/sys/net/core/somaxconn, +// to take advantage of kernels that have raised the limit. +func listenBacklog() int { return syscall.SOMAXCONN } + +// Internet sockets (TCP, UDP) + +// A sockaddr represents a TCP or UDP network address that can +// be converted into a syscall.Sockaddr. +type sockaddr interface { + Addr + sockaddr(family int) (syscall.Sockaddr, os.Error) + family() int +} + +func internetSocket(net string, laddr, raddr sockaddr, socktype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) { + var oserr os.Error + var la, ra syscall.Sockaddr + family := favoriteAddrFamily(net, raddr, laddr, mode) + if laddr != nil { + if la, oserr = laddr.sockaddr(family); oserr != nil { + goto Error + } + } + if raddr != nil { + if ra, oserr = raddr.sockaddr(family); oserr != nil { + goto Error + } + } + fd, oserr = socket(net, family, socktype, proto, la, ra, toAddr) + if oserr != nil { + goto Error + } + return fd, nil + +Error: + addr := raddr + if mode == "listen" { + addr = laddr + } + return nil, &OpError{mode, net, addr, oserr} +} + +func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { + switch family { + case syscall.AF_INET: + if len(ip) == 0 { + ip = IPv4zero + } + if ip = ip.To4(); ip == nil { + return nil, InvalidAddrError("non-IPv4 address") + } + s := new(syscall.SockaddrInet4) + for i := 0; i < IPv4len; i++ { + s.Addr[i] = ip[i] + } + s.Port = port + return s, nil + case syscall.AF_INET6: + if len(ip) == 0 { + ip = IPv6zero + } + // IPv4 callers use 0.0.0.0 to mean "announce on any available address". + // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0", + // which it refuses to do. Rewrite to the IPv6 all zeros. + if ip.Equal(IPv4zero) { + ip = IPv6zero + } + if ip = ip.To16(); ip == nil { + return nil, InvalidAddrError("non-IPv6 address") + } + s := new(syscall.SockaddrInet6) + for i := 0; i < IPv6len; i++ { + s.Addr[i] = ip[i] + } + s.Port = port + return s, nil + } + return nil, InvalidAddrError("unexpected socket family") +} diff --git a/src/pkg/net/lookup_plan9.go b/src/pkg/net/lookup_plan9.go new file mode 100644 index 000000000..37d6b8e31 --- /dev/null +++ b/src/pkg/net/lookup_plan9.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. + +package net + +import ( + "os" +) + +func query(filename, query string, bufSize int) (res []string, err os.Error) { + file, err := os.OpenFile(filename, os.O_RDWR, 0) + if err != nil { + return + } + defer file.Close() + + _, err = file.WriteString(query) + if err != nil { + return + } + _, err = file.Seek(0, 0) + if err != nil { + return + } + buf := make([]byte, bufSize) + for { + n, _ := file.Read(buf) + if n <= 0 { + break + } + res = append(res, string(buf[:n])) + } + return +} + +func queryCS(net, host, service string) (res []string, err os.Error) { + switch net { + case "tcp4", "tcp6": + net = "tcp" + case "udp4", "udp6": + net = "udp" + } + if host == "" { + host = "*" + } + return query("/net/cs", net+"!"+host+"!"+service, 128) +} + +func queryCS1(net string, ip IP, port int) (clone, dest string, err os.Error) { + ips := "*" + if !ip.IsUnspecified() { + ips = ip.String() + } + lines, err := queryCS(net, ips, itoa(port)) + if err != nil { + return + } + f := getFields(lines[0]) + if len(f) < 2 { + return "", "", os.NewError("net: bad response from ndb/cs") + } + clone, dest = f[0], f[1] + return +} + +func queryDNS(addr string, typ string) (res []string, err os.Error) { + return query("/net/dns", addr+" "+typ, 1024) +} + +// LookupHost looks up the given host using the local resolver. +// It returns an array of that host's addresses. +func LookupHost(host string) (addrs []string, err os.Error) { + // Use /net/cs insead of /net/dns because cs knows about + // host names in local network (e.g. from /lib/ndb/local) + lines, err := queryCS("tcp", host, "1") + if err != nil { + return + } + for _, line := range lines { + f := getFields(line) + if len(f) < 2 { + continue + } + addr := f[1] + if i := byteIndex(addr, '!'); i >= 0 { + addr = addr[:i] // remove port + } + if ParseIP(addr) == nil { + continue + } + addrs = append(addrs, addr) + } + return +} + +// LookupIP looks up host using the local resolver. +// It returns an array of that host's IPv4 and IPv6 addresses. +func LookupIP(host string) (ips []IP, err os.Error) { + addrs, err := LookupHost(host) + if err != nil { + return + } + for _, addr := range addrs { + if ip := ParseIP(addr); ip != nil { + ips = append(ips, ip) + } + } + return +} + +// LookupPort looks up the port for the given network and service. +func LookupPort(network, service string) (port int, err os.Error) { + switch network { + case "tcp4", "tcp6": + network = "tcp" + case "udp4", "udp6": + network = "udp" + } + lines, err := queryCS(network, "127.0.0.1", service) + if err != nil { + return + } + unknownPortError := &AddrError{"unknown port", network + "/" + service} + if len(lines) == 0 { + return 0, unknownPortError + } + f := getFields(lines[0]) + if len(f) < 2 { + return 0, unknownPortError + } + s := f[1] + if i := byteIndex(s, '!'); i >= 0 { + s = s[i+1:] // remove address + } + if n, _, ok := dtoi(s, 0); ok { + return n, nil + } + return 0, unknownPortError +} + +// LookupCNAME returns the canonical DNS host for the given name. +// Callers that do not care about the canonical name can call +// LookupHost or LookupIP directly; both take care of resolving +// the canonical name as part of the lookup. +func LookupCNAME(name string) (cname string, err os.Error) { + lines, err := queryDNS(name, "cname") + if err != nil { + return + } + if len(lines) > 0 { + if f := getFields(lines[0]); len(f) >= 3 { + return f[2] + ".", nil + } + } + return "", os.NewError("net: bad response from ndb/dns") +} + +// LookupSRV tries to resolve an SRV query of the given service, +// protocol, and domain name, as specified in RFC 2782. In most cases +// the proto argument can be the same as the corresponding +// Addr.Network(). The returned records are sorted by priority +// and randomized by weight within a priority. +func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) { + target := "_" + service + "._" + proto + "." + name + lines, err := queryDNS(target, "srv") + if err != nil { + return + } + for _, line := range lines { + f := getFields(line) + if len(f) < 6 { + continue + } + port, _, portOk := dtoi(f[2], 0) + priority, _, priorityOk := dtoi(f[3], 0) + weight, _, weightOk := dtoi(f[4], 0) + if !(portOk && priorityOk && weightOk) { + continue + } + addrs = append(addrs, &SRV{f[5], uint16(port), uint16(priority), uint16(weight)}) + cname = f[0] + } + byPriorityWeight(addrs).sort() + return +} + +// LookupMX returns the DNS MX records for the given domain name sorted by preference. +func LookupMX(name string) (mx []*MX, err os.Error) { + lines, err := queryDNS(name, "mx") + if err != nil { + return + } + for _, line := range lines { + f := getFields(line) + if len(f) < 4 { + continue + } + if pref, _, ok := dtoi(f[2], 0); ok { + mx = append(mx, &MX{f[3], uint16(pref)}) + } + } + byPref(mx).sort() + return +} + +// LookupAddr performs a reverse lookup for the given address, returning a list +// of names mapping to that address. +func LookupAddr(addr string) (name []string, err os.Error) { + arpa, err := reverseaddr(addr) + if err != nil { + return + } + lines, err := queryDNS(arpa, "ptr") + if err != nil { + return + } + for _, line := range lines { + f := getFields(line) + if len(f) < 3 { + continue + } + name = append(name, f[2]) + } + return +} diff --git a/src/pkg/net/parse_test.go b/src/pkg/net/parse_test.go index 226f354d3..8d51eba18 100644 --- a/src/pkg/net/parse_test.go +++ b/src/pkg/net/parse_test.go @@ -12,8 +12,8 @@ import ( ) func TestReadLine(t *testing.T) { - // /etc/services file does not exist on windows. - if runtime.GOOS == "windows" { + // /etc/services file does not exist on windows and Plan 9. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } filename := "/etc/services" // a nice big file diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go index 36780d789..7d7f7fc01 100644 --- a/src/pkg/net/server_test.go +++ b/src/pkg/net/server_test.go @@ -146,8 +146,8 @@ func TestTCPServer(t *testing.T) { } func TestUnixServer(t *testing.T) { - // "unix" sockets are not supported on windows. - if runtime.GOOS == "windows" { + // "unix" sockets are not supported on windows and Plan 9. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } os.Remove("/tmp/gotest.net") @@ -225,8 +225,8 @@ func TestUDPServer(t *testing.T) { } func TestUnixDatagramServer(t *testing.T) { - // "unix" sockets are not supported on windows. - if runtime.GOOS == "windows" { + // "unix" sockets are not supported on windows and Plan 9. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } for _, isEmpty := range []bool{false} { diff --git a/src/pkg/net/tcpsock.go b/src/pkg/net/tcpsock.go index 9ee6c14f7..f5c0a2781 100644 --- a/src/pkg/net/tcpsock.go +++ b/src/pkg/net/tcpsock.go @@ -7,21 +7,9 @@ package net import ( - "io" "os" - "syscall" ) -func sockaddrToTCP(sa syscall.Sockaddr) Addr { - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - return &TCPAddr{sa.Addr[0:], sa.Port} - case *syscall.SockaddrInet6: - return &TCPAddr{sa.Addr[0:], sa.Port} - } - return nil -} - // TCPAddr represents the address of a TCP end point. type TCPAddr struct { IP IP @@ -38,27 +26,6 @@ func (a *TCPAddr) String() string { return JoinHostPort(a.IP.String(), itoa(a.Port)) } -func (a *TCPAddr) family() int { - if a == nil || len(a.IP) <= 4 { - return syscall.AF_INET - } - if a.IP.To4() != nil { - return syscall.AF_INET - } - return syscall.AF_INET6 -} - -func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { - return ipToSockaddr(family, a.IP, a.Port) -} - -func (a *TCPAddr) toAddr() sockaddr { - if a == nil { // nil *TCPAddr - return nil // nil interface - } - return a -} - // ResolveTCPAddr parses addr as a TCP address of the form // host:port and resolves domain names or port names to // numeric addresses on the network net, which must be "tcp", @@ -71,234 +38,3 @@ func ResolveTCPAddr(net, addr string) (*TCPAddr, os.Error) { } return &TCPAddr{ip, port}, nil } - -// TCPConn is an implementation of the Conn interface -// for TCP network connections. -type TCPConn struct { - fd *netFD -} - -func newTCPConn(fd *netFD) *TCPConn { - c := &TCPConn{fd} - c.SetNoDelay(true) - return c -} - -func (c *TCPConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the net.Conn Read method. -func (c *TCPConn) Read(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Read(b) -} - -// ReadFrom implements the io.ReaderFrom ReadFrom method. -func (c *TCPConn) ReadFrom(r io.Reader) (int64, os.Error) { - if n, err, handled := sendFile(c.fd, r); handled { - return n, err - } - return genericReadFrom(c, r) -} - -// Write implements the net.Conn Write method. -func (c *TCPConn) Write(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the TCP connection. -func (c *TCPConn) Close() os.Error { - if !c.ok() { - return os.EINVAL - } - err := c.fd.Close() - c.fd = nil - return err -} - -// LocalAddr returns the local network address, a *TCPAddr. -func (c *TCPConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *TCPAddr. -func (c *TCPConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetTimeout implements the net.Conn SetTimeout method. -func (c *TCPConn) SetTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setTimeout(c.fd, nsec) -} - -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *TCPConn) SetReadTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadTimeout(c.fd, nsec) -} - -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *TCPConn) SetWriteTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteTimeout(c.fd, nsec) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *TCPConn) SetReadBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *TCPConn) SetWriteBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - -// SetLinger sets the behavior of Close() on a connection -// which still has data waiting to be sent or to be acknowledged. -// -// If sec < 0 (the default), Close returns immediately and -// the operating system finishes sending the data in the background. -// -// If sec == 0, Close returns immediately and the operating system -// discards any unsent or unacknowledged data. -// -// If sec > 0, Close blocks for at most sec seconds waiting for -// data to be sent and acknowledged. -func (c *TCPConn) SetLinger(sec int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setLinger(c.fd, sec) -} - -// SetKeepAlive sets whether the operating system should send -// keepalive messages on the connection. -func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error { - if !c.ok() { - return os.EINVAL - } - return setKeepAlive(c.fd, keepalive) -} - -// SetNoDelay controls whether the operating system should delay -// packet transmission in hopes of sending fewer packets -// (Nagle's algorithm). The default is true (no delay), meaning -// that data is sent as soon as possible after a Write. -func (c *TCPConn) SetNoDelay(noDelay bool) os.Error { - if !c.ok() { - return os.EINVAL - } - return setNoDelay(c.fd, noDelay) -} - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (c *TCPConn) File() (f *os.File, err os.Error) { return c.fd.dup() } - -// DialTCP connects to the remote address raddr on the network net, -// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used -// as the local address for the connection. -func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) { - if raddr == nil { - return nil, &OpError{"dial", "tcp", nil, errMissingAddress} - } - fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) - if e != nil { - return nil, e - } - return newTCPConn(fd), nil -} - -// TCPListener is a TCP network listener. -// Clients should typically use variables of type Listener -// instead of assuming TCP. -type TCPListener struct { - fd *netFD -} - -// ListenTCP announces on the TCP address laddr and returns a TCP listener. -// Net must be "tcp", "tcp4", or "tcp6". -// If laddr has a port of 0, it means to listen on some available port. -// The caller can use l.Addr() to retrieve the chosen address. -func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error) { - fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP) - if err != nil { - return nil, err - } - errno := syscall.Listen(fd.sysfd, listenBacklog()) - if errno != 0 { - closesocket(fd.sysfd) - return nil, &OpError{"listen", "tcp", laddr, os.Errno(errno)} - } - l = new(TCPListener) - l.fd = fd - return l, nil -} - -// AcceptTCP accepts the next incoming call and returns the new connection -// and the remote address. -func (l *TCPListener) AcceptTCP() (c *TCPConn, err os.Error) { - if l == nil || l.fd == nil || l.fd.sysfd < 0 { - return nil, os.EINVAL - } - fd, err := l.fd.accept(sockaddrToTCP) - if err != nil { - return nil, err - } - return newTCPConn(fd), nil -} - -// Accept implements the Accept method in the Listener interface; -// it waits for the next call and returns a generic Conn. -func (l *TCPListener) Accept() (c Conn, err os.Error) { - c1, err := l.AcceptTCP() - if err != nil { - return nil, err - } - return c1, nil -} - -// Close stops listening on the TCP address. -// Already Accepted connections are not closed. -func (l *TCPListener) Close() os.Error { - if l == nil || l.fd == nil { - return os.EINVAL - } - return l.fd.Close() -} - -// Addr returns the listener's network address, a *TCPAddr. -func (l *TCPListener) Addr() Addr { return l.fd.laddr } - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (l *TCPListener) File() (f *os.File, err os.Error) { return l.fd.dup() } diff --git a/src/pkg/net/tcpsock_plan9.go b/src/pkg/net/tcpsock_plan9.go new file mode 100644 index 000000000..f4f6e9fee --- /dev/null +++ b/src/pkg/net/tcpsock_plan9.go @@ -0,0 +1,63 @@ +// 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. + +// TCP for Plan 9 + +package net + +import ( + "os" +) + +// TCPConn is an implementation of the Conn interface +// for TCP network connections. +type TCPConn struct { + plan9Conn +} + +// DialTCP connects to the remote address raddr on the network net, +// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used +// as the local address for the connection. +func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) { + switch net { + case "tcp", "tcp4", "tcp6": + default: + return nil, UnknownNetworkError(net) + } + if raddr == nil { + return nil, &OpError{"dial", "tcp", nil, errMissingAddress} + } + c1, err := dialPlan9(net, laddr, raddr) + if err != nil { + return + } + return &TCPConn{*c1}, nil +} + +// TCPListener is a TCP network listener. +// Clients should typically use variables of type Listener +// instead of assuming TCP. +type TCPListener struct { + plan9Listener +} + +// ListenTCP announces on the TCP address laddr and returns a TCP listener. +// Net must be "tcp", "tcp4", or "tcp6". +// If laddr has a port of 0, it means to listen on some available port. +// The caller can use l.Addr() to retrieve the chosen address. +func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error) { + switch net { + case "tcp", "tcp4", "tcp6": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + return nil, &OpError{"listen", "tcp", nil, errMissingAddress} + } + l1, err := listenPlan9(net, laddr) + if err != nil { + return + } + return &TCPListener{*l1}, nil +} diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go new file mode 100644 index 000000000..5560301b4 --- /dev/null +++ b/src/pkg/net/tcpsock_posix.go @@ -0,0 +1,283 @@ +// 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. + +// TCP sockets + +package net + +import ( + "io" + "os" + "syscall" +) + +func sockaddrToTCP(sa syscall.Sockaddr) Addr { + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + return &TCPAddr{sa.Addr[0:], sa.Port} + case *syscall.SockaddrInet6: + return &TCPAddr{sa.Addr[0:], sa.Port} + } + return nil +} + +func (a *TCPAddr) family() int { + if a == nil || len(a.IP) <= 4 { + return syscall.AF_INET + } + if a.IP.To4() != nil { + return syscall.AF_INET + } + return syscall.AF_INET6 +} + +func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { + return ipToSockaddr(family, a.IP, a.Port) +} + +func (a *TCPAddr) toAddr() sockaddr { + if a == nil { // nil *TCPAddr + return nil // nil interface + } + return a +} + +// TCPConn is an implementation of the Conn interface +// for TCP network connections. +type TCPConn struct { + fd *netFD +} + +func newTCPConn(fd *netFD) *TCPConn { + c := &TCPConn{fd} + c.SetNoDelay(true) + return c +} + +func (c *TCPConn) ok() bool { return c != nil && c.fd != nil } + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *TCPConn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Read(b) +} + +// ReadFrom implements the io.ReaderFrom ReadFrom method. +func (c *TCPConn) ReadFrom(r io.Reader) (int64, os.Error) { + if n, err, handled := sendFile(c.fd, r); handled { + return n, err + } + return genericReadFrom(c, r) +} + +// Write implements the net.Conn Write method. +func (c *TCPConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b) +} + +// Close closes the TCP connection. +func (c *TCPConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close() + c.fd = nil + return err +} + +// LocalAddr returns the local network address, a *TCPAddr. +func (c *TCPConn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.laddr +} + +// RemoteAddr returns the remote network address, a *TCPAddr. +func (c *TCPConn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.raddr +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *TCPConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec) +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *TCPConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec) +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *TCPConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec) +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *TCPConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes) +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *TCPConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes) +} + +// SetLinger sets the behavior of Close() on a connection +// which still has data waiting to be sent or to be acknowledged. +// +// If sec < 0 (the default), Close returns immediately and +// the operating system finishes sending the data in the background. +// +// If sec == 0, Close returns immediately and the operating system +// discards any unsent or unacknowledged data. +// +// If sec > 0, Close blocks for at most sec seconds waiting for +// data to be sent and acknowledged. +func (c *TCPConn) SetLinger(sec int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setLinger(c.fd, sec) +} + +// SetKeepAlive sets whether the operating system should send +// keepalive messages on the connection. +func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error { + if !c.ok() { + return os.EINVAL + } + return setKeepAlive(c.fd, keepalive) +} + +// SetNoDelay controls whether the operating system should delay +// packet transmission in hopes of sending fewer packets +// (Nagle's algorithm). The default is true (no delay), meaning +// that data is sent as soon as possible after a Write. +func (c *TCPConn) SetNoDelay(noDelay bool) os.Error { + if !c.ok() { + return os.EINVAL + } + return setNoDelay(c.fd, noDelay) +} + +// File returns a copy of the underlying os.File, set to blocking mode. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +func (c *TCPConn) File() (f *os.File, err os.Error) { return c.fd.dup() } + +// DialTCP connects to the remote address raddr on the network net, +// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used +// as the local address for the connection. +func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) { + if raddr == nil { + return nil, &OpError{"dial", "tcp", nil, errMissingAddress} + } + fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) + if e != nil { + return nil, e + } + return newTCPConn(fd), nil +} + +// TCPListener is a TCP network listener. +// Clients should typically use variables of type Listener +// instead of assuming TCP. +type TCPListener struct { + fd *netFD +} + +// ListenTCP announces on the TCP address laddr and returns a TCP listener. +// Net must be "tcp", "tcp4", or "tcp6". +// If laddr has a port of 0, it means to listen on some available port. +// The caller can use l.Addr() to retrieve the chosen address. +func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error) { + fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP) + if err != nil { + return nil, err + } + errno := syscall.Listen(fd.sysfd, listenBacklog()) + if errno != 0 { + closesocket(fd.sysfd) + return nil, &OpError{"listen", "tcp", laddr, os.Errno(errno)} + } + l = new(TCPListener) + l.fd = fd + return l, nil +} + +// AcceptTCP accepts the next incoming call and returns the new connection +// and the remote address. +func (l *TCPListener) AcceptTCP() (c *TCPConn, err os.Error) { + if l == nil || l.fd == nil || l.fd.sysfd < 0 { + return nil, os.EINVAL + } + fd, err := l.fd.accept(sockaddrToTCP) + if err != nil { + return nil, err + } + return newTCPConn(fd), nil +} + +// Accept implements the Accept method in the Listener interface; +// it waits for the next call and returns a generic Conn. +func (l *TCPListener) Accept() (c Conn, err os.Error) { + c1, err := l.AcceptTCP() + if err != nil { + return nil, err + } + return c1, nil +} + +// Close stops listening on the TCP address. +// Already Accepted connections are not closed. +func (l *TCPListener) Close() os.Error { + if l == nil || l.fd == nil { + return os.EINVAL + } + return l.fd.Close() +} + +// Addr returns the listener's network address, a *TCPAddr. +func (l *TCPListener) Addr() Addr { return l.fd.laddr } + +// SetTimeout sets the deadline associated with the listener +func (l *TCPListener) SetTimeout(nsec int64) os.Error { + if l == nil || l.fd == nil { + return os.EINVAL + } + return setTimeout(l.fd, nsec) +} + +// File returns a copy of the underlying os.File, set to blocking mode. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +func (l *TCPListener) File() (f *os.File, err os.Error) { return l.fd.dup() } diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go index 94e249d62..3dfa71675 100644 --- a/src/pkg/net/udpsock.go +++ b/src/pkg/net/udpsock.go @@ -8,19 +8,8 @@ package net import ( "os" - "syscall" ) -func sockaddrToUDP(sa syscall.Sockaddr) Addr { - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - return &UDPAddr{sa.Addr[0:], sa.Port} - case *syscall.SockaddrInet6: - return &UDPAddr{sa.Addr[0:], sa.Port} - } - return nil -} - // UDPAddr represents the address of a UDP end point. type UDPAddr struct { IP IP @@ -37,27 +26,6 @@ func (a *UDPAddr) String() string { return JoinHostPort(a.IP.String(), itoa(a.Port)) } -func (a *UDPAddr) family() int { - if a == nil || len(a.IP) <= 4 { - return syscall.AF_INET - } - if a.IP.To4() != nil { - return syscall.AF_INET - } - return syscall.AF_INET6 -} - -func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { - return ipToSockaddr(family, a.IP, a.Port) -} - -func (a *UDPAddr) toAddr() sockaddr { - if a == nil { // nil *UDPAddr - return nil // nil interface - } - return a -} - // ResolveUDPAddr parses addr as a UDP address of the form // host:port and resolves domain names or port names to // numeric addresses on the network net, which must be "udp", @@ -70,254 +38,3 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error) { } return &UDPAddr{ip, port}, nil } - -// UDPConn is the implementation of the Conn and PacketConn -// interfaces for UDP network connections. -type UDPConn struct { - fd *netFD -} - -func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{fd} } - -func (c *UDPConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the net.Conn Read method. -func (c *UDPConn) Read(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Read(b) -} - -// Write implements the net.Conn Write method. -func (c *UDPConn) Write(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the UDP connection. -func (c *UDPConn) Close() os.Error { - if !c.ok() { - return os.EINVAL - } - err := c.fd.Close() - c.fd = nil - return err -} - -// LocalAddr returns the local network address. -func (c *UDPConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *UDPAddr. -func (c *UDPConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetTimeout implements the net.Conn SetTimeout method. -func (c *UDPConn) SetTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setTimeout(c.fd, nsec) -} - -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *UDPConn) SetReadTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadTimeout(c.fd, nsec) -} - -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *UDPConn) SetWriteTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteTimeout(c.fd, nsec) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *UDPConn) SetReadBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *UDPConn) SetWriteBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - -// UDP-specific methods. - -// ReadFromUDP reads a UDP packet from c, copying the payload into b. -// It returns the number of bytes copied into b and the return address -// that was on the packet. -// -// ReadFromUDP can be made to time out and return an error with Timeout() == true -// after a fixed time limit; see SetTimeout and SetReadTimeout. -func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - n, sa, err := c.fd.ReadFrom(b) - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - addr = &UDPAddr{sa.Addr[0:], sa.Port} - case *syscall.SockaddrInet6: - addr = &UDPAddr{sa.Addr[0:], sa.Port} - } - return -} - -// ReadFrom implements the net.PacketConn ReadFrom method. -func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - n, uaddr, err := c.ReadFromUDP(b) - return n, uaddr.toAddr(), err -} - -// WriteToUDP writes a UDP packet to addr via c, copying the payload from b. -// -// WriteToUDP can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. -// On packet-oriented connections, write timeouts are rare. -func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - sa, err1 := addr.sockaddr(c.fd.family) - if err1 != nil { - return 0, &OpError{Op: "write", Net: "udp", Addr: addr, Error: err1} - } - return c.fd.WriteTo(b, sa) -} - -// WriteTo implements the net.PacketConn WriteTo method. -func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - a, ok := addr.(*UDPAddr) - if !ok { - return 0, &OpError{"writeto", "udp", addr, os.EINVAL} - } - return c.WriteToUDP(b, a) -} - -// DialUDP connects to the remote address raddr on the network net, -// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used -// as the local address for the connection. -func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error) { - switch net { - case "udp", "udp4", "udp6": - default: - return nil, UnknownNetworkError(net) - } - if raddr == nil { - return nil, &OpError{"dial", "udp", nil, errMissingAddress} - } - fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) - if e != nil { - return nil, e - } - return newUDPConn(fd), nil -} - -// ListenUDP listens for incoming UDP packets addressed to the -// local address laddr. The returned connection c's ReadFrom -// and WriteTo methods can be used to receive and send UDP -// packets with per-packet addressing. -func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) { - switch net { - case "udp", "udp4", "udp6": - default: - return nil, UnknownNetworkError(net) - } - if laddr == nil { - return nil, &OpError{"listen", "udp", nil, errMissingAddress} - } - fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) - if e != nil { - return nil, e - } - return newUDPConn(fd), nil -} - -// BindToDevice binds a UDPConn to a network interface. -func (c *UDPConn) BindToDevice(device string) os.Error { - if !c.ok() { - return os.EINVAL - } - c.fd.incref() - defer c.fd.decref() - return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device)) -} - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (c *UDPConn) File() (f *os.File, err os.Error) { return c.fd.dup() } - -var errInvalidMulticast = os.NewError("invalid IPv4 multicast address") - -// JoinGroup joins the IPv4 multicast group named by addr. -// The UDPConn must use the "udp4" network. -func (c *UDPConn) JoinGroup(addr IP) os.Error { - if !c.ok() { - return os.EINVAL - } - ip := addr.To4() - if ip == nil { - return &OpError{"joingroup", "udp", &IPAddr{ip}, errInvalidMulticast} - } - mreq := &syscall.IPMreq{ - Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, - } - err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) - if err != nil { - return &OpError{"joingroup", "udp", &IPAddr{ip}, err} - } - return nil -} - -// LeaveGroup exits the IPv4 multicast group named by addr. -func (c *UDPConn) LeaveGroup(addr IP) os.Error { - if !c.ok() { - return os.EINVAL - } - ip := addr.To4() - if ip == nil { - return &OpError{"leavegroup", "udp", &IPAddr{ip}, errInvalidMulticast} - } - mreq := &syscall.IPMreq{ - Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, - } - err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)) - if err != nil { - return &OpError{"leavegroup", "udp", &IPAddr{ip}, err} - } - return nil -} diff --git a/src/pkg/net/udpsock_plan9.go b/src/pkg/net/udpsock_plan9.go new file mode 100644 index 000000000..bb7196041 --- /dev/null +++ b/src/pkg/net/udpsock_plan9.go @@ -0,0 +1,187 @@ +// 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. + +// UDP for Plan 9 + +package net + +import ( + "os" +) + +// UDPConn is the implementation of the Conn and PacketConn +// interfaces for UDP network connections. +type UDPConn struct { + plan9Conn +} + +// UDP-specific methods. + +// ReadFromUDP reads a UDP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFromUDP can be made to time out and return an error with Timeout() == true +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + if c.data == nil { + c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) + if err != nil { + return 0, nil, err + } + } + buf := make([]byte, udpHeaderSize+len(b)) + m, err := c.data.Read(buf) + if err != nil { + return + } + if m < udpHeaderSize { + return 0, nil, os.NewError("short read reading UDP header") + } + buf = buf[:m] + + h, buf := unmarshalUDPHeader(buf) + n = copy(b, buf) + return n, &UDPAddr{h.raddr, int(h.rport)}, nil +} + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + return c.ReadFromUDP(b) +} + +// WriteToUDP writes a UDP packet to addr via c, copying the payload from b. +// +// WriteToUDP can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetWriteTimeout. +// On packet-oriented connections, write timeouts are rare. +func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + if c.data == nil { + c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) + if err != nil { + return 0, err + } + } + h := new(udpHeader) + h.raddr = addr.IP.To16() + h.laddr = c.laddr.(*UDPAddr).IP.To16() + h.ifcaddr = IPv6zero // ignored (receive only) + h.rport = uint16(addr.Port) + h.lport = uint16(c.laddr.(*UDPAddr).Port) + + buf := make([]byte, udpHeaderSize+len(b)) + i := copy(buf, h.Bytes()) + copy(buf[i:], b) + return c.data.Write(buf) +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + a, ok := addr.(*UDPAddr) + if !ok { + return 0, &OpError{"writeto", "udp", addr, os.EINVAL} + } + return c.WriteToUDP(b, a) +} + +// DialUDP connects to the remote address raddr on the network net, +// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used +// as the local address for the connection. +func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) + } + if raddr == nil { + return nil, &OpError{"dial", "udp", nil, errMissingAddress} + } + c1, err := dialPlan9(net, laddr, raddr) + if err != nil { + return + } + return &UDPConn{*c1}, nil +} + +const udpHeaderSize = 16*3 + 2*2 + +type udpHeader struct { + raddr, laddr, ifcaddr IP + rport, lport uint16 +} + +func (h *udpHeader) Bytes() []byte { + b := make([]byte, udpHeaderSize) + i := 0 + i += copy(b[i:i+16], h.raddr) + i += copy(b[i:i+16], h.laddr) + i += copy(b[i:i+16], h.ifcaddr) + b[i], b[i+1], i = byte(h.rport>>8), byte(h.rport), i+2 + b[i], b[i+1], i = byte(h.lport>>8), byte(h.lport), i+2 + return b +} + +func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) { + h := new(udpHeader) + h.raddr, b = IP(b[:16]), b[16:] + h.laddr, b = IP(b[:16]), b[16:] + h.ifcaddr, b = IP(b[:16]), b[16:] + h.rport, b = uint16(b[0])<<8|uint16(b[1]), b[2:] + h.lport, b = uint16(b[0])<<8|uint16(b[1]), b[2:] + return h, b +} + +// ListenUDP listens for incoming UDP packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send UDP +// packets with per-packet addressing. +func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + return nil, &OpError{"listen", "udp", nil, errMissingAddress} + } + l, err := listenPlan9(net, laddr) + if err != nil { + return + } + _, err = l.ctl.WriteString("headers") + if err != nil { + return + } + return &UDPConn{*l.plan9Conn()}, nil +} + +// JoinGroup joins the IPv4 multicast group named by addr. +// The UDPConn must use the "udp4" network. +func (c *UDPConn) JoinGroup(addr IP) os.Error { + if !c.ok() { + return os.EINVAL + } + return os.EPLAN9 +} + +// LeaveGroup exits the IPv4 multicast group named by addr. +func (c *UDPConn) LeaveGroup(addr IP) os.Error { + if !c.ok() { + return os.EINVAL + } + return os.EPLAN9 +} diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go new file mode 100644 index 000000000..d4ea056f3 --- /dev/null +++ b/src/pkg/net/udpsock_posix.go @@ -0,0 +1,294 @@ +// 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. + +// UDP sockets + +package net + +import ( + "os" + "syscall" +) + +func sockaddrToUDP(sa syscall.Sockaddr) Addr { + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + return &UDPAddr{sa.Addr[0:], sa.Port} + case *syscall.SockaddrInet6: + return &UDPAddr{sa.Addr[0:], sa.Port} + } + return nil +} + +func (a *UDPAddr) family() int { + if a == nil || len(a.IP) <= 4 { + return syscall.AF_INET + } + if a.IP.To4() != nil { + return syscall.AF_INET + } + return syscall.AF_INET6 +} + +func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { + return ipToSockaddr(family, a.IP, a.Port) +} + +func (a *UDPAddr) toAddr() sockaddr { + if a == nil { // nil *UDPAddr + return nil // nil interface + } + return a +} + +// UDPConn is the implementation of the Conn and PacketConn +// interfaces for UDP network connections. +type UDPConn struct { + fd *netFD +} + +func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{fd} } + +func (c *UDPConn) ok() bool { return c != nil && c.fd != nil } + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *UDPConn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Read(b) +} + +// Write implements the net.Conn Write method. +func (c *UDPConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b) +} + +// Close closes the UDP connection. +func (c *UDPConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close() + c.fd = nil + return err +} + +// LocalAddr returns the local network address. +func (c *UDPConn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.laddr +} + +// RemoteAddr returns the remote network address, a *UDPAddr. +func (c *UDPConn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.raddr +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *UDPConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec) +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *UDPConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec) +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *UDPConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec) +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *UDPConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes) +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *UDPConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes) +} + +// UDP-specific methods. + +// ReadFromUDP reads a UDP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFromUDP can be made to time out and return an error with Timeout() == true +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, sa, err := c.fd.ReadFrom(b) + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + addr = &UDPAddr{sa.Addr[0:], sa.Port} + case *syscall.SockaddrInet6: + addr = &UDPAddr{sa.Addr[0:], sa.Port} + } + return +} + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, uaddr, err := c.ReadFromUDP(b) + return n, uaddr.toAddr(), err +} + +// WriteToUDP writes a UDP packet to addr via c, copying the payload from b. +// +// WriteToUDP can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetWriteTimeout. +// On packet-oriented connections, write timeouts are rare. +func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + sa, err1 := addr.sockaddr(c.fd.family) + if err1 != nil { + return 0, &OpError{Op: "write", Net: "udp", Addr: addr, Error: err1} + } + return c.fd.WriteTo(b, sa) +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + a, ok := addr.(*UDPAddr) + if !ok { + return 0, &OpError{"writeto", "udp", addr, os.EINVAL} + } + return c.WriteToUDP(b, a) +} + +// DialUDP connects to the remote address raddr on the network net, +// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used +// as the local address for the connection. +func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) + } + if raddr == nil { + return nil, &OpError{"dial", "udp", nil, errMissingAddress} + } + fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) + if e != nil { + return nil, e + } + return newUDPConn(fd), nil +} + +// ListenUDP listens for incoming UDP packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send UDP +// packets with per-packet addressing. +func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + return nil, &OpError{"listen", "udp", nil, errMissingAddress} + } + fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) + if e != nil { + return nil, e + } + return newUDPConn(fd), nil +} + +// BindToDevice binds a UDPConn to a network interface. +func (c *UDPConn) BindToDevice(device string) os.Error { + if !c.ok() { + return os.EINVAL + } + c.fd.incref() + defer c.fd.decref() + return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device)) +} + +// File returns a copy of the underlying os.File, set to blocking mode. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +func (c *UDPConn) File() (f *os.File, err os.Error) { return c.fd.dup() } + +var errInvalidMulticast = os.NewError("invalid IPv4 multicast address") + +// JoinGroup joins the IPv4 multicast group named by addr. +// The UDPConn must use the "udp4" network. +func (c *UDPConn) JoinGroup(addr IP) os.Error { + if !c.ok() { + return os.EINVAL + } + ip := addr.To4() + if ip == nil { + return &OpError{"joingroup", "udp", &IPAddr{ip}, errInvalidMulticast} + } + mreq := &syscall.IPMreq{ + Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, + } + err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) + if err != nil { + return &OpError{"joingroup", "udp", &IPAddr{ip}, err} + } + return nil +} + +// LeaveGroup exits the IPv4 multicast group named by addr. +func (c *UDPConn) LeaveGroup(addr IP) os.Error { + if !c.ok() { + return os.EINVAL + } + ip := addr.To4() + if ip == nil { + return &OpError{"leavegroup", "udp", &IPAddr{ip}, errInvalidMulticast} + } + mreq := &syscall.IPMreq{ + Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, + } + err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)) + if err != nil { + return &OpError{"leavegroup", "udp", &IPAddr{ip}, err} + } + return nil +} diff --git a/src/pkg/net/unixsock.go b/src/pkg/net/unixsock.go index 8c26a7baf..d5040f9a2 100644 --- a/src/pkg/net/unixsock.go +++ b/src/pkg/net/unixsock.go @@ -8,109 +8,14 @@ package net import ( "os" - "syscall" ) -func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err os.Error) { - var proto int - switch net { - default: - return nil, UnknownNetworkError(net) - case "unix": - proto = syscall.SOCK_STREAM - case "unixgram": - proto = syscall.SOCK_DGRAM - case "unixpacket": - proto = syscall.SOCK_SEQPACKET - } - - var la, ra syscall.Sockaddr - switch mode { - default: - panic("unixSocket mode " + mode) - - case "dial": - if laddr != nil { - la = &syscall.SockaddrUnix{Name: laddr.Name} - } - if raddr != nil { - ra = &syscall.SockaddrUnix{Name: raddr.Name} - } else if proto != syscall.SOCK_DGRAM || laddr == nil { - return nil, &OpError{Op: mode, Net: net, Error: errMissingAddress} - } - - case "listen": - if laddr == nil { - return nil, &OpError{mode, net, nil, errMissingAddress} - } - la = &syscall.SockaddrUnix{Name: laddr.Name} - if raddr != nil { - return nil, &OpError{Op: mode, Net: net, Addr: raddr, Error: &AddrError{Error: "unexpected remote address", Addr: raddr.String()}} - } - } - - f := sockaddrToUnix - if proto == syscall.SOCK_DGRAM { - f = sockaddrToUnixgram - } else if proto == syscall.SOCK_SEQPACKET { - f = sockaddrToUnixpacket - } - - fd, oserr := socket(net, syscall.AF_UNIX, proto, 0, la, ra, f) - if oserr != nil { - goto Error - } - return fd, nil - -Error: - addr := raddr - if mode == "listen" { - addr = laddr - } - return nil, &OpError{Op: mode, Net: net, Addr: addr, Error: oserr} -} - // UnixAddr represents the address of a Unix domain socket end point. type UnixAddr struct { Name string Net string } -func sockaddrToUnix(sa syscall.Sockaddr) Addr { - if s, ok := sa.(*syscall.SockaddrUnix); ok { - return &UnixAddr{s.Name, "unix"} - } - return nil -} - -func sockaddrToUnixgram(sa syscall.Sockaddr) Addr { - if s, ok := sa.(*syscall.SockaddrUnix); ok { - return &UnixAddr{s.Name, "unixgram"} - } - return nil -} - -func sockaddrToUnixpacket(sa syscall.Sockaddr) Addr { - if s, ok := sa.(*syscall.SockaddrUnix); ok { - return &UnixAddr{s.Name, "unixpacket"} - } - return nil -} - -func protoToNet(proto int) string { - switch proto { - case syscall.SOCK_STREAM: - return "unix" - case syscall.SOCK_SEQPACKET: - return "unixpacket" - case syscall.SOCK_DGRAM: - return "unixgram" - default: - panic("protoToNet unknown protocol") - } - return "" -} - // Network returns the address's network name, "unix" or "unixgram". func (a *UnixAddr) Network() string { return a.Net @@ -143,307 +48,3 @@ func ResolveUnixAddr(net, addr string) (*UnixAddr, os.Error) { } return &UnixAddr{addr, net}, nil } - -// UnixConn is an implementation of the Conn interface -// for connections to Unix domain sockets. -type UnixConn struct { - fd *netFD -} - -func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{fd} } - -func (c *UnixConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the net.Conn Read method. -func (c *UnixConn) Read(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Read(b) -} - -// Write implements the net.Conn Write method. -func (c *UnixConn) Write(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the Unix domain connection. -func (c *UnixConn) Close() os.Error { - if !c.ok() { - return os.EINVAL - } - err := c.fd.Close() - c.fd = nil - return err -} - -// LocalAddr returns the local network address, a *UnixAddr. -// Unlike in other protocols, LocalAddr is usually nil for dialed connections. -func (c *UnixConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *UnixAddr. -// Unlike in other protocols, RemoteAddr is usually nil for connections -// accepted by a listener. -func (c *UnixConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetTimeout implements the net.Conn SetTimeout method. -func (c *UnixConn) SetTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setTimeout(c.fd, nsec) -} - -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *UnixConn) SetReadTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadTimeout(c.fd, nsec) -} - -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *UnixConn) SetWriteTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteTimeout(c.fd, nsec) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *UnixConn) SetReadBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *UnixConn) SetWriteBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - -// ReadFromUnix reads a packet from c, copying the payload into b. -// It returns the number of bytes copied into b and the return address -// that was on the packet. -// -// ReadFromUnix can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetReadTimeout. -func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - n, sa, err := c.fd.ReadFrom(b) - switch sa := sa.(type) { - case *syscall.SockaddrUnix: - addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} - } - return -} - -// ReadFrom implements the net.PacketConn ReadFrom method. -func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - n, uaddr, err := c.ReadFromUnix(b) - return n, uaddr.toAddr(), err -} - -// WriteToUnix writes a packet to addr via c, copying the payload from b. -// -// WriteToUnix can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. -// On packet-oriented connections, write timeouts are rare. -func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - if addr.Net != protoToNet(c.fd.proto) { - return 0, os.EAFNOSUPPORT - } - sa := &syscall.SockaddrUnix{Name: addr.Name} - return c.fd.WriteTo(b, sa) -} - -// WriteTo implements the net.PacketConn WriteTo method. -func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - a, ok := addr.(*UnixAddr) - if !ok { - return 0, &OpError{"writeto", "unix", addr, os.EINVAL} - } - return c.WriteToUnix(b, a) -} - -func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err os.Error) { - if !c.ok() { - return 0, 0, 0, nil, os.EINVAL - } - n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob) - switch sa := sa.(type) { - case *syscall.SockaddrUnix: - addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} - } - return -} - -func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err os.Error) { - if !c.ok() { - return 0, 0, os.EINVAL - } - if addr != nil { - if addr.Net != protoToNet(c.fd.proto) { - return 0, 0, os.EAFNOSUPPORT - } - sa := &syscall.SockaddrUnix{Name: addr.Name} - return c.fd.WriteMsg(b, oob, sa) - } - return c.fd.WriteMsg(b, oob, nil) -} - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (c *UnixConn) File() (f *os.File, err os.Error) { return c.fd.dup() } - -// DialUnix connects to the remote address raddr on the network net, -// which must be "unix" or "unixgram". If laddr is not nil, it is used -// as the local address for the connection. -func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err os.Error) { - fd, e := unixSocket(net, laddr, raddr, "dial") - if e != nil { - return nil, e - } - return newUnixConn(fd), nil -} - -// UnixListener is a Unix domain socket listener. -// Clients should typically use variables of type Listener -// instead of assuming Unix domain sockets. -type UnixListener struct { - fd *netFD - path string -} - -// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. -// Net must be "unix" (stream sockets). -func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) { - if net != "unix" && net != "unixgram" && net != "unixpacket" { - return nil, UnknownNetworkError(net) - } - if laddr != nil { - laddr = &UnixAddr{laddr.Name, net} // make our own copy - } - fd, err := unixSocket(net, laddr, nil, "listen") - if err != nil { - return nil, err - } - e1 := syscall.Listen(fd.sysfd, 8) // listenBacklog()); - if e1 != 0 { - closesocket(fd.sysfd) - return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Error: os.Errno(e1)} - } - return &UnixListener{fd, laddr.Name}, nil -} - -// AcceptUnix accepts the next incoming call and returns the new connection -// and the remote address. -func (l *UnixListener) AcceptUnix() (c *UnixConn, err os.Error) { - if l == nil || l.fd == nil { - return nil, os.EINVAL - } - fd, e := l.fd.accept(sockaddrToUnix) - if e != nil { - return nil, e - } - c = newUnixConn(fd) - return c, nil -} - -// Accept implements the Accept method in the Listener interface; -// it waits for the next call and returns a generic Conn. -func (l *UnixListener) Accept() (c Conn, err os.Error) { - c1, err := l.AcceptUnix() - if err != nil { - return nil, err - } - return c1, nil -} - -// Close stops listening on the Unix address. -// Already accepted connections are not closed. -func (l *UnixListener) Close() os.Error { - if l == nil || l.fd == nil { - return os.EINVAL - } - - // The operating system doesn't clean up - // the file that announcing created, so - // we have to clean it up ourselves. - // There's a race here--we can't know for - // sure whether someone else has come along - // and replaced our socket name already-- - // but this sequence (remove then close) - // is at least compatible with the auto-remove - // sequence in ListenUnix. It's only non-Go - // programs that can mess us up. - if l.path[0] != '@' { - syscall.Unlink(l.path) - } - err := l.fd.Close() - l.fd = nil - return err -} - -// Addr returns the listener's network address. -func (l *UnixListener) Addr() Addr { return l.fd.laddr } - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (l *UnixListener) File() (f *os.File, err os.Error) { return l.fd.dup() } - -// ListenUnixgram listens for incoming Unix datagram packets addressed to the -// local address laddr. The returned connection c's ReadFrom -// and WriteTo methods can be used to receive and send UDP -// packets with per-packet addressing. The network net must be "unixgram". -func ListenUnixgram(net string, laddr *UnixAddr) (c *UDPConn, err os.Error) { - switch net { - case "unixgram": - default: - return nil, UnknownNetworkError(net) - } - if laddr == nil { - return nil, &OpError{"listen", "unixgram", nil, errMissingAddress} - } - fd, e := unixSocket(net, laddr, nil, "listen") - if e != nil { - return nil, e - } - return newUDPConn(fd), nil -} diff --git a/src/pkg/net/unixsock_plan9.go b/src/pkg/net/unixsock_plan9.go new file mode 100644 index 000000000..7e212df8a --- /dev/null +++ b/src/pkg/net/unixsock_plan9.go @@ -0,0 +1,105 @@ +// 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. + +// Unix domain sockets stubs for Plan 9 + +package net + +import ( + "os" +) + +// UnixConn is an implementation of the Conn interface +// for connections to Unix domain sockets. +type UnixConn bool + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *UnixConn) Read(b []byte) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +// Write implements the net.Conn Write method. +func (c *UnixConn) Write(b []byte) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +// Close closes the Unix domain connection. +func (c *UnixConn) Close() os.Error { + return os.EPLAN9 +} + +// LocalAddr returns the local network address, a *UnixAddr. +// Unlike in other protocols, LocalAddr is usually nil for dialed connections. +func (c *UnixConn) LocalAddr() Addr { + return nil +} + +// RemoteAddr returns the remote network address, a *UnixAddr. +// Unlike in other protocols, RemoteAddr is usually nil for connections +// accepted by a listener. +func (c *UnixConn) RemoteAddr() Addr { + return nil +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *UnixConn) SetTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *UnixConn) SetReadTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *UnixConn) SetWriteTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + err = os.EPLAN9 + return +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + err = os.EPLAN9 + return +} + +// DialUnix connects to the remote address raddr on the network net, +// which must be "unix" or "unixgram". If laddr is not nil, it is used +// as the local address for the connection. +func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err os.Error) { + return nil, os.EPLAN9 +} + +// UnixListener is a Unix domain socket listener. +// Clients should typically use variables of type Listener +// instead of assuming Unix domain sockets. +type UnixListener bool + +// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. +// Net must be "unix" (stream sockets). +func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) { + return nil, os.EPLAN9 +} + +// Accept implements the Accept method in the Listener interface; +// it waits for the next call and returns a generic Conn. +func (l *UnixListener) Accept() (c Conn, err os.Error) { + return nil, os.EPLAN9 +} + +// Close stops listening on the Unix address. +// Already accepted connections are not closed. +func (l *UnixListener) Close() os.Error { + return os.EPLAN9 +} + +// Addr returns the listener's network address. +func (l *UnixListener) Addr() Addr { return nil } diff --git a/src/pkg/net/unixsock_posix.go b/src/pkg/net/unixsock_posix.go new file mode 100644 index 000000000..38c6fe9eb --- /dev/null +++ b/src/pkg/net/unixsock_posix.go @@ -0,0 +1,418 @@ +// 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. + +// Unix domain sockets + +package net + +import ( + "os" + "syscall" +) + +func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err os.Error) { + var proto int + switch net { + default: + return nil, UnknownNetworkError(net) + case "unix": + proto = syscall.SOCK_STREAM + case "unixgram": + proto = syscall.SOCK_DGRAM + case "unixpacket": + proto = syscall.SOCK_SEQPACKET + } + + var la, ra syscall.Sockaddr + switch mode { + default: + panic("unixSocket mode " + mode) + + case "dial": + if laddr != nil { + la = &syscall.SockaddrUnix{Name: laddr.Name} + } + if raddr != nil { + ra = &syscall.SockaddrUnix{Name: raddr.Name} + } else if proto != syscall.SOCK_DGRAM || laddr == nil { + return nil, &OpError{Op: mode, Net: net, Error: errMissingAddress} + } + + case "listen": + if laddr == nil { + return nil, &OpError{mode, net, nil, errMissingAddress} + } + la = &syscall.SockaddrUnix{Name: laddr.Name} + if raddr != nil { + return nil, &OpError{Op: mode, Net: net, Addr: raddr, Error: &AddrError{Error: "unexpected remote address", Addr: raddr.String()}} + } + } + + f := sockaddrToUnix + if proto == syscall.SOCK_DGRAM { + f = sockaddrToUnixgram + } else if proto == syscall.SOCK_SEQPACKET { + f = sockaddrToUnixpacket + } + + fd, oserr := socket(net, syscall.AF_UNIX, proto, 0, la, ra, f) + if oserr != nil { + goto Error + } + return fd, nil + +Error: + addr := raddr + if mode == "listen" { + addr = laddr + } + return nil, &OpError{Op: mode, Net: net, Addr: addr, Error: oserr} +} + +func sockaddrToUnix(sa syscall.Sockaddr) Addr { + if s, ok := sa.(*syscall.SockaddrUnix); ok { + return &UnixAddr{s.Name, "unix"} + } + return nil +} + +func sockaddrToUnixgram(sa syscall.Sockaddr) Addr { + if s, ok := sa.(*syscall.SockaddrUnix); ok { + return &UnixAddr{s.Name, "unixgram"} + } + return nil +} + +func sockaddrToUnixpacket(sa syscall.Sockaddr) Addr { + if s, ok := sa.(*syscall.SockaddrUnix); ok { + return &UnixAddr{s.Name, "unixpacket"} + } + return nil +} + +func protoToNet(proto int) string { + switch proto { + case syscall.SOCK_STREAM: + return "unix" + case syscall.SOCK_SEQPACKET: + return "unixpacket" + case syscall.SOCK_DGRAM: + return "unixgram" + default: + panic("protoToNet unknown protocol") + } + return "" +} + +// UnixConn is an implementation of the Conn interface +// for connections to Unix domain sockets. +type UnixConn struct { + fd *netFD +} + +func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{fd} } + +func (c *UnixConn) ok() bool { return c != nil && c.fd != nil } + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *UnixConn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Read(b) +} + +// Write implements the net.Conn Write method. +func (c *UnixConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b) +} + +// Close closes the Unix domain connection. +func (c *UnixConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close() + c.fd = nil + return err +} + +// LocalAddr returns the local network address, a *UnixAddr. +// Unlike in other protocols, LocalAddr is usually nil for dialed connections. +func (c *UnixConn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.laddr +} + +// RemoteAddr returns the remote network address, a *UnixAddr. +// Unlike in other protocols, RemoteAddr is usually nil for connections +// accepted by a listener. +func (c *UnixConn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.raddr +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *UnixConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec) +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *UnixConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec) +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *UnixConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec) +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *UnixConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes) +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *UnixConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes) +} + +// ReadFromUnix reads a packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFromUnix can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetReadTimeout. +func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, sa, err := c.fd.ReadFrom(b) + switch sa := sa.(type) { + case *syscall.SockaddrUnix: + addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} + } + return +} + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, uaddr, err := c.ReadFromUnix(b) + return n, uaddr.toAddr(), err +} + +// WriteToUnix writes a packet to addr via c, copying the payload from b. +// +// WriteToUnix can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetWriteTimeout. +// On packet-oriented connections, write timeouts are rare. +func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + if addr.Net != protoToNet(c.fd.proto) { + return 0, os.EAFNOSUPPORT + } + sa := &syscall.SockaddrUnix{Name: addr.Name} + return c.fd.WriteTo(b, sa) +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + a, ok := addr.(*UnixAddr) + if !ok { + return 0, &OpError{"writeto", "unix", addr, os.EINVAL} + } + return c.WriteToUnix(b, a) +} + +func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err os.Error) { + if !c.ok() { + return 0, 0, 0, nil, os.EINVAL + } + n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob) + switch sa := sa.(type) { + case *syscall.SockaddrUnix: + addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} + } + return +} + +func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err os.Error) { + if !c.ok() { + return 0, 0, os.EINVAL + } + if addr != nil { + if addr.Net != protoToNet(c.fd.proto) { + return 0, 0, os.EAFNOSUPPORT + } + sa := &syscall.SockaddrUnix{Name: addr.Name} + return c.fd.WriteMsg(b, oob, sa) + } + return c.fd.WriteMsg(b, oob, nil) +} + +// File returns a copy of the underlying os.File, set to blocking mode. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +func (c *UnixConn) File() (f *os.File, err os.Error) { return c.fd.dup() } + +// DialUnix connects to the remote address raddr on the network net, +// which must be "unix" or "unixgram". If laddr is not nil, it is used +// as the local address for the connection. +func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err os.Error) { + fd, e := unixSocket(net, laddr, raddr, "dial") + if e != nil { + return nil, e + } + return newUnixConn(fd), nil +} + +// UnixListener is a Unix domain socket listener. +// Clients should typically use variables of type Listener +// instead of assuming Unix domain sockets. +type UnixListener struct { + fd *netFD + path string +} + +// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. +// Net must be "unix" (stream sockets). +func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) { + if net != "unix" && net != "unixgram" && net != "unixpacket" { + return nil, UnknownNetworkError(net) + } + if laddr != nil { + laddr = &UnixAddr{laddr.Name, net} // make our own copy + } + fd, err := unixSocket(net, laddr, nil, "listen") + if err != nil { + return nil, err + } + e1 := syscall.Listen(fd.sysfd, 8) // listenBacklog()); + if e1 != 0 { + closesocket(fd.sysfd) + return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Error: os.Errno(e1)} + } + return &UnixListener{fd, laddr.Name}, nil +} + +// AcceptUnix accepts the next incoming call and returns the new connection +// and the remote address. +func (l *UnixListener) AcceptUnix() (c *UnixConn, err os.Error) { + if l == nil || l.fd == nil { + return nil, os.EINVAL + } + fd, e := l.fd.accept(sockaddrToUnix) + if e != nil { + return nil, e + } + c = newUnixConn(fd) + return c, nil +} + +// Accept implements the Accept method in the Listener interface; +// it waits for the next call and returns a generic Conn. +func (l *UnixListener) Accept() (c Conn, err os.Error) { + c1, err := l.AcceptUnix() + if err != nil { + return nil, err + } + return c1, nil +} + +// Close stops listening on the Unix address. +// Already accepted connections are not closed. +func (l *UnixListener) Close() os.Error { + if l == nil || l.fd == nil { + return os.EINVAL + } + + // The operating system doesn't clean up + // the file that announcing created, so + // we have to clean it up ourselves. + // There's a race here--we can't know for + // sure whether someone else has come along + // and replaced our socket name already-- + // but this sequence (remove then close) + // is at least compatible with the auto-remove + // sequence in ListenUnix. It's only non-Go + // programs that can mess us up. + if l.path[0] != '@' { + syscall.Unlink(l.path) + } + err := l.fd.Close() + l.fd = nil + return err +} + +// Addr returns the listener's network address. +func (l *UnixListener) Addr() Addr { return l.fd.laddr } + +// SetTimeout sets the deadline associated wuth the listener +func (l *UnixListener) SetTimeout(nsec int64) (err os.Error) { + if l == nil || l.fd == nil { + return os.EINVAL + } + return setTimeout(l.fd, nsec) +} + +// File returns a copy of the underlying os.File, set to blocking mode. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +func (l *UnixListener) File() (f *os.File, err os.Error) { return l.fd.dup() } + +// ListenUnixgram listens for incoming Unix datagram packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send UDP +// packets with per-packet addressing. The network net must be "unixgram". +func ListenUnixgram(net string, laddr *UnixAddr) (c *UDPConn, err os.Error) { + switch net { + case "unixgram": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + return nil, &OpError{"listen", "unixgram", nil, errMissingAddress} + } + fd, e := unixSocket(net, laddr, nil, "listen") + if e != nil { + return nil, e + } + return newUDPConn(fd), nil +} diff --git a/src/pkg/old/template/Makefile b/src/pkg/old/template/Makefile new file mode 100644 index 000000000..5772cb4aa --- /dev/null +++ b/src/pkg/old/template/Makefile @@ -0,0 +1,14 @@ +# 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. + +include ../../../Make.inc + +TARG=old/template +GOFILES=\ + doc.go\ + execute.go\ + format.go\ + parse.go\ + +include ../../../Make.pkg diff --git a/src/pkg/old/template/doc.go b/src/pkg/old/template/doc.go new file mode 100644 index 000000000..e778d801d --- /dev/null +++ b/src/pkg/old/template/doc.go @@ -0,0 +1,91 @@ +// 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 template implements data-driven templates for generating textual + output such as HTML. + + Templates are executed by applying them to a data structure. + Annotations in the template refer to elements of the data + structure (typically a field of a struct or a key in a map) + to control execution and derive values to be displayed. + The template walks the structure as it executes and the + "cursor" @ represents the value at the current location + in the structure. + + Data items may be values or pointers; the interface hides the + indirection. + + In the following, 'Field' is one of several things, according to the data. + + - The name of a field of a struct (result = data.Field), + - The value stored in a map under that key (result = data["Field"]), or + - The result of invoking a niladic single-valued method with that name + (result = data.Field()) + + If Field is a struct field or method name, it must be an exported + (capitalized) name. + + Major constructs ({} are the default delimiters for template actions; + [] are the notation in this comment for optional elements): + + {# comment } + + A one-line comment. + + {.section field} XXX [ {.or} YYY ] {.end} + + Set @ to the value of the field. It may be an explicit @ + to stay at the same point in the data. If the field is nil + or empty, execute YYY; otherwise execute XXX. + + {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end} + + Like .section, but field must be an array or slice. XXX + is executed for each element. If the array is nil or empty, + YYY is executed instead. If the {.alternates with} marker + is present, ZZZ is executed between iterations of XXX. + + {field} + {field1 field2 ...} + {field|formatter} + {field1 field2...|formatter} + {field|formatter1|formatter2} + + Insert the value of the fields into the output. Each field is + first looked for in the cursor, as in .section and .repeated. + If it is not found, the search continues in outer sections + until the top level is reached. + + If the field value is a pointer, leading asterisks indicate + that the value to be inserted should be evaluated through the + pointer. For example, if x.p is of type *int, {x.p} will + insert the value of the pointer but {*x.p} will insert the + value of the underlying integer. If the value is nil or not a + pointer, asterisks have no effect. + + If a formatter is specified, it must be named in the formatter + map passed to the template set up routines or in the default + set ("html","str","") and is used to process the data for + output. The formatter function has signature + func(wr io.Writer, formatter string, data ...interface{}) + where wr is the destination for output, data holds the field + values at the instantiation, and formatter is its name at + the invocation site. The default formatter just concatenates + the string representations of the fields. + + Multiple formatters separated by the pipeline character | are + executed sequentially, with each formatter receiving the bytes + emitted by the one to its left. + + As well as field names, one may use literals with Go syntax. + Integer, floating-point, and string literals are supported. + Raw strings may not span newlines. + + The delimiter strings get their default value, "{" and "}", from + JSON-template. They may be set to any non-empty, space-free + string using the SetDelims method. Their value can be printed + in the output using {.meta-left} and {.meta-right}. +*/ +package template diff --git a/src/pkg/old/template/execute.go b/src/pkg/old/template/execute.go new file mode 100644 index 000000000..464b620c9 --- /dev/null +++ b/src/pkg/old/template/execute.go @@ -0,0 +1,346 @@ +// 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. + +// Code to execute a parsed template. + +package template + +import ( + "bytes" + "io" + "reflect" + "strings" +) + +// Internal state for executing a Template. As we evaluate the struct, +// the data item descends into the fields associated with sections, etc. +// Parent is used to walk upwards to find variables higher in the tree. +type state struct { + parent *state // parent in hierarchy + data reflect.Value // the driver data for this section etc. + wr io.Writer // where to send output + buf [2]bytes.Buffer // alternating buffers used when chaining formatters +} + +func (parent *state) clone(data reflect.Value) *state { + return &state{parent: parent, data: data, wr: parent.wr} +} + +// Evaluate interfaces and pointers looking for a value that can look up the name, via a +// struct field, method, or map key, and return the result of the lookup. +func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value { + for v.IsValid() { + typ := v.Type() + if n := v.Type().NumMethod(); n > 0 { + for i := 0; i < n; i++ { + m := typ.Method(i) + mtyp := m.Type + if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { + if !isExported(name) { + t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) + } + return v.Method(i).Call(nil)[0] + } + } + } + switch av := v; av.Kind() { + case reflect.Ptr: + v = av.Elem() + case reflect.Interface: + v = av.Elem() + case reflect.Struct: + if !isExported(name) { + t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) + } + return av.FieldByName(name) + case reflect.Map: + if v := av.MapIndex(reflect.ValueOf(name)); v.IsValid() { + return v + } + return reflect.Zero(typ.Elem()) + default: + return reflect.Value{} + } + } + return v +} + +// indirectPtr returns the item numLevels levels of indirection below the value. +// It is forgiving: if the value is not a pointer, it returns it rather than giving +// an error. If the pointer is nil, it is returned as is. +func indirectPtr(v reflect.Value, numLevels int) reflect.Value { + for i := numLevels; v.IsValid() && i > 0; i++ { + if p := v; p.Kind() == reflect.Ptr { + if p.IsNil() { + return v + } + v = p.Elem() + } else { + break + } + } + return v +} + +// Walk v through pointers and interfaces, extracting the elements within. +func indirect(v reflect.Value) reflect.Value { +loop: + for v.IsValid() { + switch av := v; av.Kind() { + case reflect.Ptr: + v = av.Elem() + case reflect.Interface: + v = av.Elem() + default: + break loop + } + } + return v +} + +// If the data for this template is a struct, find the named variable. +// Names of the form a.b.c are walked down the data tree. +// The special name "@" (the "cursor") denotes the current data. +// The value coming in (st.data) might need indirecting to reach +// a struct while the return value is not indirected - that is, +// it represents the actual named field. Leading stars indicate +// levels of indirection to be applied to the value. +func (t *Template) findVar(st *state, s string) reflect.Value { + data := st.data + flattenedName := strings.TrimLeft(s, "*") + numStars := len(s) - len(flattenedName) + s = flattenedName + if s == "@" { + return indirectPtr(data, numStars) + } + for _, elem := range strings.Split(s, ".") { + // Look up field; data must be a struct or map. + data = t.lookup(st, data, elem) + if !data.IsValid() { + return reflect.Value{} + } + } + return indirectPtr(data, numStars) +} + +// Is there no data to look at? +func empty(v reflect.Value) bool { + v = indirect(v) + if !v.IsValid() { + return true + } + switch v.Kind() { + case reflect.Bool: + return v.Bool() == false + case reflect.String: + return v.String() == "" + case reflect.Struct: + return false + case reflect.Map: + return false + case reflect.Array: + return v.Len() == 0 + case reflect.Slice: + return v.Len() == 0 + } + return false +} + +// Look up a variable or method, up through the parent if necessary. +func (t *Template) varValue(name string, st *state) reflect.Value { + field := t.findVar(st, name) + if !field.IsValid() { + if st.parent == nil { + t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type()) + } + return t.varValue(name, st.parent) + } + return field +} + +func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) { + fn := t.formatter(fmt) + if fn == nil { + t.execError(st, v.linenum, "missing formatter %s for variable", fmt) + } + fn(wr, fmt, val...) +} + +// Evaluate a variable, looking up through the parent if necessary. +// If it has a formatter attached ({var|formatter}) run that too. +func (t *Template) writeVariable(v *variableElement, st *state) { + // Resolve field names + val := make([]interface{}, len(v.args)) + for i, arg := range v.args { + if name, ok := arg.(fieldName); ok { + val[i] = t.varValue(string(name), st).Interface() + } else { + val[i] = arg + } + } + for i, fmt := range v.fmts[:len(v.fmts)-1] { + b := &st.buf[i&1] + b.Reset() + t.format(b, fmt, val, v, st) + val = val[0:1] + val[0] = b.Bytes() + } + t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st) +} + +// Execute element i. Return next index to execute. +func (t *Template) executeElement(i int, st *state) int { + switch elem := t.elems[i].(type) { + case *textElement: + st.wr.Write(elem.text) + return i + 1 + case *literalElement: + st.wr.Write(elem.text) + return i + 1 + case *variableElement: + t.writeVariable(elem, st) + return i + 1 + case *sectionElement: + t.executeSection(elem, st) + return elem.end + case *repeatedElement: + t.executeRepeated(elem, st) + return elem.end + } + e := t.elems[i] + t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.ValueOf(e).Interface(), e) + return 0 +} + +// Execute the template. +func (t *Template) execute(start, end int, st *state) { + for i := start; i < end; { + i = t.executeElement(i, st) + } +} + +// Execute a .section +func (t *Template) executeSection(s *sectionElement, st *state) { + // Find driver data for this section. It must be in the current struct. + field := t.varValue(s.field, st) + if !field.IsValid() { + t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type()) + } + st = st.clone(field) + start, end := s.start, s.or + if !empty(field) { + // Execute the normal block. + if end < 0 { + end = s.end + } + } else { + // Execute the .or block. If it's missing, do nothing. + start, end = s.or, s.end + if start < 0 { + return + } + } + for i := start; i < end; { + i = t.executeElement(i, st) + } +} + +// Return the result of calling the Iter method on v, or nil. +func iter(v reflect.Value) reflect.Value { + for j := 0; j < v.Type().NumMethod(); j++ { + mth := v.Type().Method(j) + fv := v.Method(j) + ft := fv.Type() + // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue. + if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 { + continue + } + ct := ft.Out(0) + if ct.Kind() != reflect.Chan || + ct.ChanDir()&reflect.RecvDir == 0 { + continue + } + return fv.Call(nil)[0] + } + return reflect.Value{} +} + +// Execute a .repeated section +func (t *Template) executeRepeated(r *repeatedElement, st *state) { + // Find driver data for this section. It must be in the current struct. + field := t.varValue(r.field, st) + if !field.IsValid() { + t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type()) + } + field = indirect(field) + + start, end := r.start, r.or + if end < 0 { + end = r.end + } + if r.altstart >= 0 { + end = r.altstart + } + first := true + + // Code common to all the loops. + loopBody := func(newst *state) { + // .alternates between elements + if !first && r.altstart >= 0 { + for i := r.altstart; i < r.altend; { + i = t.executeElement(i, newst) + } + } + first = false + for i := start; i < end; { + i = t.executeElement(i, newst) + } + } + + if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice { + for j := 0; j < array.Len(); j++ { + loopBody(st.clone(array.Index(j))) + } + } else if m := field; m.Kind() == reflect.Map { + for _, key := range m.MapKeys() { + loopBody(st.clone(m.MapIndex(key))) + } + } else if ch := iter(field); ch.IsValid() { + for { + e, ok := ch.Recv() + if !ok { + break + } + loopBody(st.clone(e)) + } + } else { + t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)", + r.field, field.Type()) + } + + if first { + // Empty. Execute the .or block, once. If it's missing, do nothing. + start, end := r.or, r.end + if start >= 0 { + newst := st.clone(field) + for i := start; i < end; { + i = t.executeElement(i, newst) + } + } + return + } +} + +// A valid delimiter must contain no space and be non-empty. +func validDelim(d []byte) bool { + if len(d) == 0 { + return false + } + for _, c := range d { + if isSpace(c) { + return false + } + } + return true +} diff --git a/src/pkg/old/template/format.go b/src/pkg/old/template/format.go new file mode 100644 index 000000000..9156b0808 --- /dev/null +++ b/src/pkg/old/template/format.go @@ -0,0 +1,77 @@ +// 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. + +// Template library: default formatters + +package template + +import ( + "bytes" + "fmt" + "io" +) + +// StringFormatter formats into the default string representation. +// It is stored under the name "str" and is the default formatter. +// You can override the default formatter by storing your default +// under the name "" in your custom formatter map. +func StringFormatter(w io.Writer, format string, value ...interface{}) { + if len(value) == 1 { + if b, ok := value[0].([]byte); ok { + w.Write(b) + return + } + } + fmt.Fprint(w, value...) +} + +var ( + esc_quot = []byte(""") // shorter than """ + esc_apos = []byte("'") // shorter than "'" + esc_amp = []byte("&") + esc_lt = []byte("<") + esc_gt = []byte(">") +) + +// HTMLEscape writes to w the properly escaped HTML equivalent +// of the plain text data s. +func HTMLEscape(w io.Writer, s []byte) { + var esc []byte + last := 0 + for i, c := range s { + switch c { + case '"': + esc = esc_quot + case '\'': + esc = esc_apos + case '&': + esc = esc_amp + case '<': + esc = esc_lt + case '>': + esc = esc_gt + default: + continue + } + w.Write(s[last:i]) + w.Write(esc) + last = i + 1 + } + w.Write(s[last:]) +} + +// HTMLFormatter formats arbitrary values for HTML +func HTMLFormatter(w io.Writer, format string, value ...interface{}) { + ok := false + var b []byte + if len(value) == 1 { + b, ok = value[0].([]byte) + } + if !ok { + var buf bytes.Buffer + fmt.Fprint(&buf, value...) + b = buf.Bytes() + } + HTMLEscape(w, b) +} diff --git a/src/pkg/old/template/parse.go b/src/pkg/old/template/parse.go new file mode 100644 index 000000000..dedf9ad8e --- /dev/null +++ b/src/pkg/old/template/parse.go @@ -0,0 +1,743 @@ +// 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. + +// Code to parse a template. + +package template + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "reflect" + "strconv" + "strings" + "unicode" + "utf8" +) + +// Errors returned during parsing and execution. Users may extract the information and reformat +// if they desire. +type Error struct { + Line int + Msg string +} + +func (e *Error) String() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) } + +// checkError is a deferred function to turn a panic with type *Error into a plain error return. +// Other panics are unexpected and so are re-enabled. +func checkError(error *os.Error) { + if v := recover(); v != nil { + if e, ok := v.(*Error); ok { + *error = e + } else { + // runtime errors should crash + panic(v) + } + } +} + +// Most of the literals are aces. +var lbrace = []byte{'{'} +var rbrace = []byte{'}'} +var space = []byte{' '} +var tab = []byte{'\t'} + +// The various types of "tokens", which are plain text or (usually) brace-delimited descriptors +const ( + tokAlternates = iota + tokComment + tokEnd + tokLiteral + tokOr + tokRepeated + tokSection + tokText + tokVariable +) + +// FormatterMap is the type describing the mapping from formatter +// names to the functions that implement them. +type FormatterMap map[string]func(io.Writer, string, ...interface{}) + +// Built-in formatters. +var builtins = FormatterMap{ + "html": HTMLFormatter, + "str": StringFormatter, + "": StringFormatter, +} + +// The parsed state of a template is a vector of xxxElement structs. +// Sections have line numbers so errors can be reported better during execution. + +// Plain text. +type textElement struct { + text []byte +} + +// A literal such as .meta-left or .meta-right +type literalElement struct { + text []byte +} + +// A variable invocation to be evaluated +type variableElement struct { + linenum int + args []interface{} // The fields and literals in the invocation. + fmts []string // Names of formatters to apply. len(fmts) > 0 +} + +// A variableElement arg to be evaluated as a field name +type fieldName string + +// A .section block, possibly with a .or +type sectionElement struct { + linenum int // of .section itself + field string // cursor field for this block + start int // first element + or int // first element of .or block + end int // one beyond last element +} + +// A .repeated block, possibly with a .or and a .alternates +type repeatedElement struct { + sectionElement // It has the same structure... + altstart int // ... except for alternates + altend int +} + +// Template is the type that represents a template definition. +// It is unchanged after parsing. +type Template struct { + fmap FormatterMap // formatters for variables + // Used during parsing: + ldelim, rdelim []byte // delimiters; default {} + buf []byte // input text to process + p int // position in buf + linenum int // position in input + // Parsed results: + elems []interface{} +} + +// New creates a new template with the specified formatter map (which +// may be nil) to define auxiliary functions for formatting variables. +func New(fmap FormatterMap) *Template { + t := new(Template) + t.fmap = fmap + t.ldelim = lbrace + t.rdelim = rbrace + t.elems = make([]interface{}, 0, 16) + return t +} + +// Report error and stop executing. The line number must be provided explicitly. +func (t *Template) execError(st *state, line int, err string, args ...interface{}) { + panic(&Error{line, fmt.Sprintf(err, args...)}) +} + +// Report error, panic to terminate parsing. +// The line number comes from the template state. +func (t *Template) parseError(err string, args ...interface{}) { + panic(&Error{t.linenum, fmt.Sprintf(err, args...)}) +} + +// Is this an exported - upper case - name? +func isExported(name string) bool { + rune, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(rune) +} + +// -- Lexical analysis + +// Is c a space character? +func isSpace(c uint8) bool { return c == ' ' || c == '\t' || c == '\r' || c == '\n' } + +// Safely, does s[n:n+len(t)] == t? +func equal(s []byte, n int, t []byte) bool { + b := s[n:] + if len(t) > len(b) { // not enough space left for a match. + return false + } + for i, c := range t { + if c != b[i] { + return false + } + } + return true +} + +// isQuote returns true if c is a string- or character-delimiting quote character. +func isQuote(c byte) bool { + return c == '"' || c == '`' || c == '\'' +} + +// endQuote returns the end quote index for the quoted string that +// starts at n, or -1 if no matching end quote is found before the end +// of the line. +func endQuote(s []byte, n int) int { + quote := s[n] + for n++; n < len(s); n++ { + switch s[n] { + case '\\': + if quote == '"' || quote == '\'' { + n++ + } + case '\n': + return -1 + case quote: + return n + } + } + return -1 +} + +// nextItem returns the next item from the input buffer. If the returned +// item is empty, we are at EOF. The item will be either a +// delimited string or a non-empty string between delimited +// strings. Tokens stop at (but include, if plain text) a newline. +// Action tokens on a line by themselves drop any space on +// either side, up to and including the newline. +func (t *Template) nextItem() []byte { + startOfLine := t.p == 0 || t.buf[t.p-1] == '\n' + start := t.p + var i int + newline := func() { + t.linenum++ + i++ + } + // Leading space up to but not including newline + for i = start; i < len(t.buf); i++ { + if t.buf[i] == '\n' || !isSpace(t.buf[i]) { + break + } + } + leadingSpace := i > start + // What's left is nothing, newline, delimited string, or plain text + switch { + case i == len(t.buf): + // EOF; nothing to do + case t.buf[i] == '\n': + newline() + case equal(t.buf, i, t.ldelim): + left := i // Start of left delimiter. + right := -1 // Will be (immediately after) right delimiter. + haveText := false // Delimiters contain text. + i += len(t.ldelim) + // Find the end of the action. + for ; i < len(t.buf); i++ { + if t.buf[i] == '\n' { + break + } + if isQuote(t.buf[i]) { + i = endQuote(t.buf, i) + if i == -1 { + t.parseError("unmatched quote") + return nil + } + continue + } + if equal(t.buf, i, t.rdelim) { + i += len(t.rdelim) + right = i + break + } + haveText = true + } + if right < 0 { + t.parseError("unmatched opening delimiter") + return nil + } + // Is this a special action (starts with '.' or '#') and the only thing on the line? + if startOfLine && haveText { + firstChar := t.buf[left+len(t.ldelim)] + if firstChar == '.' || firstChar == '#' { + // It's special and the first thing on the line. Is it the last? + for j := right; j < len(t.buf) && isSpace(t.buf[j]); j++ { + if t.buf[j] == '\n' { + // Yes it is. Drop the surrounding space and return the {.foo} + t.linenum++ + t.p = j + 1 + return t.buf[left:right] + } + } + } + } + // No it's not. If there's leading space, return that. + if leadingSpace { + // not trimming space: return leading space if there is some. + t.p = left + return t.buf[start:left] + } + // Return the word, leave the trailing space. + start = left + break + default: + for ; i < len(t.buf); i++ { + if t.buf[i] == '\n' { + newline() + break + } + if equal(t.buf, i, t.ldelim) { + break + } + } + } + item := t.buf[start:i] + t.p = i + return item +} + +// Turn a byte array into a space-split array of strings, +// taking into account quoted strings. +func words(buf []byte) []string { + s := make([]string, 0, 5) + for i := 0; i < len(buf); { + // One word per loop + for i < len(buf) && isSpace(buf[i]) { + i++ + } + if i == len(buf) { + break + } + // Got a word + start := i + if isQuote(buf[i]) { + i = endQuote(buf, i) + if i < 0 { + i = len(buf) + } else { + i++ + } + } + // Even with quotes, break on space only. This handles input + // such as {""|} and catches quoting mistakes. + for i < len(buf) && !isSpace(buf[i]) { + i++ + } + s = append(s, string(buf[start:i])) + } + return s +} + +// Analyze an item and return its token type and, if it's an action item, an array of +// its constituent words. +func (t *Template) analyze(item []byte) (tok int, w []string) { + // item is known to be non-empty + if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter + tok = tokText + return + } + if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter + t.parseError("internal error: unmatched opening delimiter") // lexing should prevent this + return + } + if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents + t.parseError("empty directive") + return + } + // Comment + if item[len(t.ldelim)] == '#' { + tok = tokComment + return + } + // Split into words + w = words(item[len(t.ldelim) : len(item)-len(t.rdelim)]) // drop final delimiter + if len(w) == 0 { + t.parseError("empty directive") + return + } + first := w[0] + if first[0] != '.' { + tok = tokVariable + return + } + if len(first) > 1 && first[1] >= '0' && first[1] <= '9' { + // Must be a float. + tok = tokVariable + return + } + switch first { + case ".meta-left", ".meta-right", ".space", ".tab": + tok = tokLiteral + return + case ".or": + tok = tokOr + return + case ".end": + tok = tokEnd + return + case ".section": + if len(w) != 2 { + t.parseError("incorrect fields for .section: %s", item) + return + } + tok = tokSection + return + case ".repeated": + if len(w) != 3 || w[1] != "section" { + t.parseError("incorrect fields for .repeated: %s", item) + return + } + tok = tokRepeated + return + case ".alternates": + if len(w) != 2 || w[1] != "with" { + t.parseError("incorrect fields for .alternates: %s", item) + return + } + tok = tokAlternates + return + } + t.parseError("bad directive: %s", item) + return +} + +// formatter returns the Formatter with the given name in the Template, or nil if none exists. +func (t *Template) formatter(name string) func(io.Writer, string, ...interface{}) { + if t.fmap != nil { + if fn := t.fmap[name]; fn != nil { + return fn + } + } + return builtins[name] +} + +// -- Parsing + +// newVariable allocates a new variable-evaluation element. +func (t *Template) newVariable(words []string) *variableElement { + formatters := extractFormatters(words) + args := make([]interface{}, len(words)) + + // Build argument list, processing any literals + for i, word := range words { + var lerr os.Error + switch word[0] { + case '"', '`', '\'': + v, err := strconv.Unquote(word) + if err == nil && word[0] == '\'' { + args[i] = []int(v)[0] + } else { + args[i], lerr = v, err + } + + case '.', '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + v, err := strconv.Btoi64(word, 0) + if err == nil { + args[i] = v + } else { + v, err := strconv.Atof64(word) + args[i], lerr = v, err + } + + default: + args[i] = fieldName(word) + } + if lerr != nil { + t.parseError("invalid literal: %q: %s", word, lerr) + } + } + + // We could remember the function address here and avoid the lookup later, + // but it's more dynamic to let the user change the map contents underfoot. + // We do require the name to be present, though. + + // Is it in user-supplied map? + for _, f := range formatters { + if t.formatter(f) == nil { + t.parseError("unknown formatter: %q", f) + } + } + + return &variableElement{t.linenum, args, formatters} +} + +// extractFormatters extracts a list of formatters from words. +// After the final space-separated argument in a variable, formatters may be +// specified separated by pipe symbols. For example: {a b c|d|e} +// The words parameter still has the formatters joined by '|' in the last word. +// extractFormatters splits formatters, replaces the last word with the content +// found before the first '|' within it, and returns the formatters obtained. +// If no formatters are found in words, the default formatter is returned. +func extractFormatters(words []string) (formatters []string) { + // "" is the default formatter. + formatters = []string{""} + if len(words) == 0 { + return + } + var bar int + lastWord := words[len(words)-1] + if isQuote(lastWord[0]) { + end := endQuote([]byte(lastWord), 0) + if end < 0 || end+1 == len(lastWord) || lastWord[end+1] != '|' { + return + } + bar = end + 1 + } else { + bar = strings.IndexRune(lastWord, '|') + if bar < 0 { + return + } + } + words[len(words)-1] = lastWord[0:bar] + formatters = strings.Split(lastWord[bar+1:], "|") + return +} + +// Grab the next item. If it's simple, just append it to the template. +// Otherwise return its details. +func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { + tok, w = t.analyze(item) + done = true // assume for simplicity + switch tok { + case tokComment: + return + case tokText: + t.elems = append(t.elems, &textElement{item}) + return + case tokLiteral: + switch w[0] { + case ".meta-left": + t.elems = append(t.elems, &literalElement{t.ldelim}) + case ".meta-right": + t.elems = append(t.elems, &literalElement{t.rdelim}) + case ".space": + t.elems = append(t.elems, &literalElement{space}) + case ".tab": + t.elems = append(t.elems, &literalElement{tab}) + default: + t.parseError("internal error: unknown literal: %s", w[0]) + } + return + case tokVariable: + t.elems = append(t.elems, t.newVariable(w)) + return + } + return false, tok, w +} + +// parseRepeated and parseSection are mutually recursive + +func (t *Template) parseRepeated(words []string) *repeatedElement { + r := new(repeatedElement) + t.elems = append(t.elems, r) + r.linenum = t.linenum + r.field = words[2] + // Scan section, collecting true and false (.or) blocks. + r.start = len(t.elems) + r.or = -1 + r.altstart = -1 + r.altend = -1 +Loop: + for { + item := t.nextItem() + if len(item) == 0 { + t.parseError("missing .end for .repeated section") + break + } + done, tok, w := t.parseSimple(item) + if done { + continue + } + switch tok { + case tokEnd: + break Loop + case tokOr: + if r.or >= 0 { + t.parseError("extra .or in .repeated section") + break Loop + } + r.altend = len(t.elems) + r.or = len(t.elems) + case tokSection: + t.parseSection(w) + case tokRepeated: + t.parseRepeated(w) + case tokAlternates: + if r.altstart >= 0 { + t.parseError("extra .alternates in .repeated section") + break Loop + } + if r.or >= 0 { + t.parseError(".alternates inside .or block in .repeated section") + break Loop + } + r.altstart = len(t.elems) + default: + t.parseError("internal error: unknown repeated section item: %s", item) + break Loop + } + } + if r.altend < 0 { + r.altend = len(t.elems) + } + r.end = len(t.elems) + return r +} + +func (t *Template) parseSection(words []string) *sectionElement { + s := new(sectionElement) + t.elems = append(t.elems, s) + s.linenum = t.linenum + s.field = words[1] + // Scan section, collecting true and false (.or) blocks. + s.start = len(t.elems) + s.or = -1 +Loop: + for { + item := t.nextItem() + if len(item) == 0 { + t.parseError("missing .end for .section") + break + } + done, tok, w := t.parseSimple(item) + if done { + continue + } + switch tok { + case tokEnd: + break Loop + case tokOr: + if s.or >= 0 { + t.parseError("extra .or in .section") + break Loop + } + s.or = len(t.elems) + case tokSection: + t.parseSection(w) + case tokRepeated: + t.parseRepeated(w) + case tokAlternates: + t.parseError(".alternates not in .repeated") + default: + t.parseError("internal error: unknown section item: %s", item) + } + } + s.end = len(t.elems) + return s +} + +func (t *Template) parse() { + for { + item := t.nextItem() + if len(item) == 0 { + break + } + done, tok, w := t.parseSimple(item) + if done { + continue + } + switch tok { + case tokOr, tokEnd, tokAlternates: + t.parseError("unexpected %s", w[0]) + case tokSection: + t.parseSection(w) + case tokRepeated: + t.parseRepeated(w) + default: + t.parseError("internal error: bad directive in parse: %s", item) + } + } +} + +// -- Execution + +// -- Public interface + +// Parse initializes a Template by parsing its definition. The string +// s contains the template text. If any errors occur, Parse returns +// the error. +func (t *Template) Parse(s string) (err os.Error) { + if t.elems == nil { + return &Error{1, "template not allocated with New"} + } + if !validDelim(t.ldelim) || !validDelim(t.rdelim) { + return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)} + } + defer checkError(&err) + t.buf = []byte(s) + t.p = 0 + t.linenum = 1 + t.parse() + return nil +} + +// ParseFile is like Parse but reads the template definition from the +// named file. +func (t *Template) ParseFile(filename string) (err os.Error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + return t.Parse(string(b)) +} + +// Execute applies a parsed template to the specified data object, +// generating output to wr. +func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) { + // Extract the driver data. + val := reflect.ValueOf(data) + defer checkError(&err) + t.p = 0 + t.execute(0, len(t.elems), &state{parent: nil, data: val, wr: wr}) + return nil +} + +// SetDelims sets the left and right delimiters for operations in the +// template. They are validated during parsing. They could be +// validated here but it's better to keep the routine simple. The +// delimiters are very rarely invalid and Parse has the necessary +// error-handling interface already. +func (t *Template) SetDelims(left, right string) { + t.ldelim = []byte(left) + t.rdelim = []byte(right) +} + +// Parse creates a Template with default parameters (such as {} for +// metacharacters). The string s contains the template text while +// the formatter map fmap, which may be nil, defines auxiliary functions +// for formatting variables. The template is returned. If any errors +// occur, err will be non-nil. +func Parse(s string, fmap FormatterMap) (t *Template, err os.Error) { + t = New(fmap) + err = t.Parse(s) + if err != nil { + t = nil + } + return +} + +// ParseFile is a wrapper function that creates a Template with default +// parameters (such as {} for metacharacters). The filename identifies +// a file containing the template text, while the formatter map fmap, which +// may be nil, defines auxiliary functions for formatting variables. +// The template is returned. If any errors occur, err will be non-nil. +func ParseFile(filename string, fmap FormatterMap) (t *Template, err os.Error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return Parse(string(b), fmap) +} + +// MustParse is like Parse but panics if the template cannot be parsed. +func MustParse(s string, fmap FormatterMap) *Template { + t, err := Parse(s, fmap) + if err != nil { + panic("template.MustParse error: " + err.String()) + } + return t +} + +// MustParseFile is like ParseFile but panics if the file cannot be read +// or the template cannot be parsed. +func MustParseFile(filename string, fmap FormatterMap) *Template { + b, err := ioutil.ReadFile(filename) + if err != nil { + panic("template.MustParseFile error: " + err.String()) + } + return MustParse(string(b), fmap) +} diff --git a/src/pkg/old/template/template_test.go b/src/pkg/old/template/template_test.go new file mode 100644 index 000000000..eae8011eb --- /dev/null +++ b/src/pkg/old/template/template_test.go @@ -0,0 +1,804 @@ +// 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 template + +import ( + "bytes" + "container/vector" + "fmt" + "io" + "io/ioutil" + "json" + "os" + "strings" + "testing" +) + +type Test struct { + in, out, err string +} + +type T struct { + Item string + Value string +} + +type U struct { + Mp map[string]int +} + +type S struct { + Header string + HeaderPtr *string + Integer int + IntegerPtr *int + NilPtr *int + InnerT T + InnerPointerT *T + Data []T + Pdata []*T + Empty []*T + Emptystring string + Null []*T + Vec *vector.Vector + True bool + False bool + Mp map[string]string + JSON interface{} + Innermap U + Stringmap map[string]string + Ptrmap map[string]*string + Iface interface{} + Ifaceptr interface{} +} + +func (s *S) PointerMethod() string { return "ptrmethod!" } + +func (s S) ValueMethod() string { return "valmethod!" } + +var t1 = T{"ItemNumber1", "ValueNumber1"} +var t2 = T{"ItemNumber2", "ValueNumber2"} + +func uppercase(v interface{}) string { + s := v.(string) + t := "" + for i := 0; i < len(s); i++ { + c := s[i] + if 'a' <= c && c <= 'z' { + c = c + 'A' - 'a' + } + t += string(c) + } + return t +} + +func plus1(v interface{}) string { + i := v.(int) + return fmt.Sprint(i + 1) +} + +func writer(f func(interface{}) string) func(io.Writer, string, ...interface{}) { + return func(w io.Writer, format string, v ...interface{}) { + if len(v) != 1 { + panic("test writer expected one arg") + } + io.WriteString(w, f(v[0])) + } +} + +func multiword(w io.Writer, format string, value ...interface{}) { + for _, v := range value { + fmt.Fprintf(w, "<%v>", v) + } +} + +func printf(w io.Writer, format string, v ...interface{}) { + io.WriteString(w, fmt.Sprintf(v[0].(string), v[1:]...)) +} + +var formatters = FormatterMap{ + "uppercase": writer(uppercase), + "+1": writer(plus1), + "multiword": multiword, + "printf": printf, +} + +var tests = []*Test{ + // Simple + &Test{"", "", ""}, + &Test{"abc", "abc", ""}, + &Test{"abc\ndef\n", "abc\ndef\n", ""}, + &Test{" {.meta-left} \n", "{", ""}, + &Test{" {.meta-right} \n", "}", ""}, + &Test{" {.space} \n", " ", ""}, + &Test{" {.tab} \n", "\t", ""}, + &Test{" {#comment} \n", "", ""}, + &Test{"\tSome Text\t\n", "\tSome Text\t\n", ""}, + &Test{" {.meta-right} {.meta-right} {.meta-right} \n", " } } } \n", ""}, + + // Variables at top level + &Test{ + in: "{Header}={Integer}\n", + + out: "Header=77\n", + }, + + &Test{ + in: "Pointers: {*HeaderPtr}={*IntegerPtr}\n", + + out: "Pointers: Header=77\n", + }, + + &Test{ + in: "Stars but not pointers: {*Header}={*Integer}\n", + + out: "Stars but not pointers: Header=77\n", + }, + + &Test{ + in: "nil pointer: {*NilPtr}={*Integer}\n", + + out: "nil pointer: =77\n", + }, + + &Test{ + in: `{"Strings" ":"} {""} {"|"} {"\t\u0123 \x23\\"} {"\"}{\\"}`, + + out: "Strings: | \t\u0123 \x23\\ \"}{\\", + }, + + &Test{ + in: "{`Raw strings` `:`} {``} {`|`} {`\\t\\u0123 \\x23\\`} {`}{\\`}", + + out: "Raw strings: | \\t\\u0123 \\x23\\ }{\\", + }, + + &Test{ + in: "Characters: {'a'} {'\\u0123'} {' '} {'{'} {'|'} {'}'}", + + out: "Characters: 97 291 32 123 124 125", + }, + + &Test{ + in: "Integers: {1} {-2} {+42} {0777} {0x0a}", + + out: "Integers: 1 -2 42 511 10", + }, + + &Test{ + in: "Floats: {.5} {-.5} {1.1} {-2.2} {+42.1} {1e10} {1.2e-3} {1.2e3} {-1.2e3}", + + out: "Floats: 0.5 -0.5 1.1 -2.2 42.1 1e+10 0.0012 1200 -1200", + }, + + // Method at top level + &Test{ + in: "ptrmethod={PointerMethod}\n", + + out: "ptrmethod=ptrmethod!\n", + }, + + &Test{ + in: "valmethod={ValueMethod}\n", + + out: "valmethod=valmethod!\n", + }, + + // Section + &Test{ + in: "{.section Data }\n" + + "some text for the section\n" + + "{.end}\n", + + out: "some text for the section\n", + }, + &Test{ + in: "{.section Data }\n" + + "{Header}={Integer}\n" + + "{.end}\n", + + out: "Header=77\n", + }, + &Test{ + in: "{.section Pdata }\n" + + "{Header}={Integer}\n" + + "{.end}\n", + + out: "Header=77\n", + }, + &Test{ + in: "{.section Pdata }\n" + + "data present\n" + + "{.or}\n" + + "data not present\n" + + "{.end}\n", + + out: "data present\n", + }, + &Test{ + in: "{.section Empty }\n" + + "data present\n" + + "{.or}\n" + + "data not present\n" + + "{.end}\n", + + out: "data not present\n", + }, + &Test{ + in: "{.section Null }\n" + + "data present\n" + + "{.or}\n" + + "data not present\n" + + "{.end}\n", + + out: "data not present\n", + }, + &Test{ + in: "{.section Pdata }\n" + + "{Header}={Integer}\n" + + "{.section @ }\n" + + "{Header}={Integer}\n" + + "{.end}\n" + + "{.end}\n", + + out: "Header=77\n" + + "Header=77\n", + }, + + &Test{ + in: "{.section Data}{.end} {Header}\n", + + out: " Header\n", + }, + + &Test{ + in: "{.section Integer}{@}{.end}", + + out: "77", + }, + + // Repeated + &Test{ + in: "{.section Pdata }\n" + + "{.repeated section @ }\n" + + "{Item}={Value}\n" + + "{.end}\n" + + "{.end}\n", + + out: "ItemNumber1=ValueNumber1\n" + + "ItemNumber2=ValueNumber2\n", + }, + &Test{ + in: "{.section Pdata }\n" + + "{.repeated section @ }\n" + + "{Item}={Value}\n" + + "{.or}\n" + + "this should not appear\n" + + "{.end}\n" + + "{.end}\n", + + out: "ItemNumber1=ValueNumber1\n" + + "ItemNumber2=ValueNumber2\n", + }, + &Test{ + in: "{.section @ }\n" + + "{.repeated section Empty }\n" + + "{Item}={Value}\n" + + "{.or}\n" + + "this should appear: empty field\n" + + "{.end}\n" + + "{.end}\n", + + out: "this should appear: empty field\n", + }, + &Test{ + in: "{.repeated section Pdata }\n" + + "{Item}\n" + + "{.alternates with}\n" + + "is\nover\nmultiple\nlines\n" + + "{.end}\n", + + out: "ItemNumber1\n" + + "is\nover\nmultiple\nlines\n" + + "ItemNumber2\n", + }, + &Test{ + in: "{.repeated section Pdata }\n" + + "{Item}\n" + + "{.alternates with}\n" + + "is\nover\nmultiple\nlines\n" + + " {.end}\n", + + out: "ItemNumber1\n" + + "is\nover\nmultiple\nlines\n" + + "ItemNumber2\n", + }, + &Test{ + in: "{.section Pdata }\n" + + "{.repeated section @ }\n" + + "{Item}={Value}\n" + + "{.alternates with}DIVIDER\n" + + "{.or}\n" + + "this should not appear\n" + + "{.end}\n" + + "{.end}\n", + + out: "ItemNumber1=ValueNumber1\n" + + "DIVIDER\n" + + "ItemNumber2=ValueNumber2\n", + }, + &Test{ + in: "{.repeated section Vec }\n" + + "{@}\n" + + "{.end}\n", + + out: "elt1\n" + + "elt2\n", + }, + // Same but with a space before {.end}: was a bug. + &Test{ + in: "{.repeated section Vec }\n" + + "{@} {.end}\n", + + out: "elt1 elt2 \n", + }, + &Test{ + in: "{.repeated section Integer}{.end}", + + err: "line 1: .repeated: cannot repeat Integer (type int)", + }, + + // Nested names + &Test{ + in: "{.section @ }\n" + + "{InnerT.Item}={InnerT.Value}\n" + + "{.end}", + + out: "ItemNumber1=ValueNumber1\n", + }, + &Test{ + in: "{.section @ }\n" + + "{InnerT.Item}={.section InnerT}{.section Value}{@}{.end}{.end}\n" + + "{.end}", + + out: "ItemNumber1=ValueNumber1\n", + }, + + &Test{ + in: "{.section Emptystring}emptystring{.end}\n" + + "{.section Header}header{.end}\n", + + out: "\nheader\n", + }, + + &Test{ + in: "{.section True}1{.or}2{.end}\n" + + "{.section False}3{.or}4{.end}\n", + + out: "1\n4\n", + }, + + // Maps + + &Test{ + in: "{Mp.mapkey}\n", + + out: "Ahoy!\n", + }, + &Test{ + in: "{Innermap.Mp.innerkey}\n", + + out: "55\n", + }, + &Test{ + in: "{.section Innermap}{.section Mp}{innerkey}{.end}{.end}\n", + + out: "55\n", + }, + &Test{ + in: "{.section JSON}{.repeated section maps}{a}{b}{.end}{.end}\n", + + out: "1234\n", + }, + &Test{ + in: "{Stringmap.stringkey1}\n", + + out: "stringresult\n", + }, + &Test{ + in: "{.repeated section Stringmap}\n" + + "{@}\n" + + "{.end}", + + out: "stringresult\n" + + "stringresult\n", + }, + &Test{ + in: "{.repeated section Stringmap}\n" + + "\t{@}\n" + + "{.end}", + + out: "\tstringresult\n" + + "\tstringresult\n", + }, + &Test{ + in: "{*Ptrmap.stringkey1}\n", + + out: "pointedToString\n", + }, + &Test{ + in: "{.repeated section Ptrmap}\n" + + "{*@}\n" + + "{.end}", + + out: "pointedToString\n" + + "pointedToString\n", + }, + + // Interface values + + &Test{ + in: "{Iface}", + + out: "[1 2 3]", + }, + &Test{ + in: "{.repeated section Iface}{@}{.alternates with} {.end}", + + out: "1 2 3", + }, + &Test{ + in: "{.section Iface}{@}{.end}", + + out: "[1 2 3]", + }, + &Test{ + in: "{.section Ifaceptr}{Item} {Value}{.end}", + + out: "Item Value", + }, +} + +func TestAll(t *testing.T) { + // Parse + testAll(t, func(test *Test) (*Template, os.Error) { return Parse(test.in, formatters) }) + // ParseFile + testAll(t, func(test *Test) (*Template, os.Error) { + err := ioutil.WriteFile("_test/test.tmpl", []byte(test.in), 0600) + if err != nil { + t.Error("unexpected write error:", err) + return nil, err + } + return ParseFile("_test/test.tmpl", formatters) + }) + // tmpl.ParseFile + testAll(t, func(test *Test) (*Template, os.Error) { + err := ioutil.WriteFile("_test/test.tmpl", []byte(test.in), 0600) + if err != nil { + t.Error("unexpected write error:", err) + return nil, err + } + tmpl := New(formatters) + return tmpl, tmpl.ParseFile("_test/test.tmpl") + }) +} + +func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) { + s := new(S) + // initialized by hand for clarity. + s.Header = "Header" + s.HeaderPtr = &s.Header + s.Integer = 77 + s.IntegerPtr = &s.Integer + s.InnerT = t1 + s.Data = []T{t1, t2} + s.Pdata = []*T{&t1, &t2} + s.Empty = []*T{} + s.Null = nil + s.Vec = new(vector.Vector) + s.Vec.Push("elt1") + s.Vec.Push("elt2") + s.True = true + s.False = false + s.Mp = make(map[string]string) + s.Mp["mapkey"] = "Ahoy!" + json.Unmarshal([]byte(`{"maps":[{"a":1,"b":2},{"a":3,"b":4}]}`), &s.JSON) + s.Innermap.Mp = make(map[string]int) + s.Innermap.Mp["innerkey"] = 55 + s.Stringmap = make(map[string]string) + s.Stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent + s.Stringmap["stringkey2"] = "stringresult" + s.Ptrmap = make(map[string]*string) + x := "pointedToString" + s.Ptrmap["stringkey1"] = &x // the same value so repeated section is order-independent + s.Ptrmap["stringkey2"] = &x + s.Iface = []int{1, 2, 3} + s.Ifaceptr = &T{"Item", "Value"} + + var buf bytes.Buffer + for _, test := range tests { + buf.Reset() + tmpl, err := parseFunc(test) + if err != nil { + t.Error("unexpected parse error: ", err) + continue + } + err = tmpl.Execute(&buf, s) + if test.err == "" { + if err != nil { + t.Error("unexpected execute error:", err) + } + } else { + if err == nil { + t.Errorf("expected execute error %q, got nil", test.err) + } else if err.String() != test.err { + t.Errorf("expected execute error %q, got %q", test.err, err.String()) + } + } + if buf.String() != test.out { + t.Errorf("for %q: expected %q got %q", test.in, test.out, buf.String()) + } + } +} + +func TestMapDriverType(t *testing.T) { + mp := map[string]string{"footer": "Ahoy!"} + tmpl, err := Parse("template: {footer}", nil) + if err != nil { + t.Error("unexpected parse error:", err) + } + var b bytes.Buffer + err = tmpl.Execute(&b, mp) + if err != nil { + t.Error("unexpected execute error:", err) + } + s := b.String() + expect := "template: Ahoy!" + if s != expect { + t.Errorf("failed passing string as data: expected %q got %q", expect, s) + } +} + +func TestMapNoEntry(t *testing.T) { + mp := make(map[string]int) + tmpl, err := Parse("template: {notthere}!", nil) + if err != nil { + t.Error("unexpected parse error:", err) + } + var b bytes.Buffer + err = tmpl.Execute(&b, mp) + if err != nil { + t.Error("unexpected execute error:", err) + } + s := b.String() + expect := "template: 0!" + if s != expect { + t.Errorf("failed passing string as data: expected %q got %q", expect, s) + } +} + +func TestStringDriverType(t *testing.T) { + tmpl, err := Parse("template: {@}", nil) + if err != nil { + t.Error("unexpected parse error:", err) + } + var b bytes.Buffer + err = tmpl.Execute(&b, "hello") + if err != nil { + t.Error("unexpected execute error:", err) + } + s := b.String() + expect := "template: hello" + if s != expect { + t.Errorf("failed passing string as data: expected %q got %q", expect, s) + } +} + +func TestTwice(t *testing.T) { + tmpl, err := Parse("template: {@}", nil) + if err != nil { + t.Error("unexpected parse error:", err) + } + var b bytes.Buffer + err = tmpl.Execute(&b, "hello") + if err != nil { + t.Error("unexpected parse error:", err) + } + s := b.String() + expect := "template: hello" + if s != expect { + t.Errorf("failed passing string as data: expected %q got %q", expect, s) + } + err = tmpl.Execute(&b, "hello") + if err != nil { + t.Error("unexpected parse error:", err) + } + s = b.String() + expect += expect + if s != expect { + t.Errorf("failed passing string as data: expected %q got %q", expect, s) + } +} + +func TestCustomDelims(t *testing.T) { + // try various lengths. zero should catch error. + for i := 0; i < 7; i++ { + for j := 0; j < 7; j++ { + tmpl := New(nil) + // first two chars deliberately the same to test equal left and right delims + ldelim := "$!#$%^&"[0:i] + rdelim := "$*&^%$!"[0:j] + tmpl.SetDelims(ldelim, rdelim) + // if braces, this would be template: {@}{.meta-left}{.meta-right} + text := "template: " + + ldelim + "@" + rdelim + + ldelim + ".meta-left" + rdelim + + ldelim + ".meta-right" + rdelim + err := tmpl.Parse(text) + if err != nil { + if i == 0 || j == 0 { // expected + continue + } + t.Error("unexpected parse error:", err) + } else if i == 0 || j == 0 { + t.Errorf("expected parse error for empty delimiter: %d %d %q %q", i, j, ldelim, rdelim) + continue + } + var b bytes.Buffer + err = tmpl.Execute(&b, "hello") + s := b.String() + if s != "template: hello"+ldelim+rdelim { + t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s) + } + } + } +} + +// Test that a variable evaluates to the field itself and does not further indirection +func TestVarIndirection(t *testing.T) { + s := new(S) + // initialized by hand for clarity. + s.InnerPointerT = &t1 + + var buf bytes.Buffer + input := "{.section @}{InnerPointerT}{.end}" + tmpl, err := Parse(input, nil) + if err != nil { + t.Fatal("unexpected parse error:", err) + } + err = tmpl.Execute(&buf, s) + if err != nil { + t.Fatal("unexpected execute error:", err) + } + expect := fmt.Sprintf("%v", &t1) // output should be hex address of t1 + if buf.String() != expect { + t.Errorf("for %q: expected %q got %q", input, expect, buf.String()) + } +} + +func TestHTMLFormatterWithByte(t *testing.T) { + s := "Test string." + b := []byte(s) + var buf bytes.Buffer + HTMLFormatter(&buf, "", b) + bs := buf.String() + if bs != s { + t.Errorf("munged []byte, expected: %s got: %s", s, bs) + } +} + +type UF struct { + I int + s string +} + +func TestReferenceToUnexported(t *testing.T) { + u := &UF{3, "hello"} + var buf bytes.Buffer + input := "{.section @}{I}{s}{.end}" + tmpl, err := Parse(input, nil) + if err != nil { + t.Fatal("unexpected parse error:", err) + } + err = tmpl.Execute(&buf, u) + if err == nil { + t.Fatal("expected execute error, got none") + } + if strings.Index(err.String(), "not exported") < 0 { + t.Fatal("expected unexported error; got", err) + } +} + +var formatterTests = []Test{ + { + in: "{Header|uppercase}={Integer|+1}\n" + + "{Header|html}={Integer|str}\n", + + out: "HEADER=78\n" + + "Header=77\n", + }, + + { + in: "{Header|uppercase}={Integer Header|multiword}\n" + + "{Header|html}={Header Integer|multiword}\n" + + "{Header|html}={Header Integer}\n", + + out: "HEADER=<77>

\n" + + "Header=
<77>\n" + + "Header=Header77\n", + }, + { + in: "{Raw}\n" + + "{Raw|html}\n", + + out: "a <&> b\n" + + "a <&> b\n", + }, + { + in: "{Bytes}", + out: "hello", + }, + { + in: "{Raw|uppercase|html|html}", + out: "A &lt;&amp;&gt; B", + }, + { + in: "{Header Integer|multiword|html}", + out: "<Header><77>", + }, + { + in: "{Integer|no_formatter|html}", + err: `unknown formatter: "no_formatter"`, + }, + { + in: "{Integer|||||}", // empty string is a valid formatter + out: "77", + }, + { + in: `{"%.02f 0x%02X" 1.1 10|printf}`, + out: "1.10 0x0A", + }, + { + in: `{""|}{""||}{""|printf}`, // Issue #1896. + out: "", + }, +} + +func TestFormatters(t *testing.T) { + data := map[string]interface{}{ + "Header": "Header", + "Integer": 77, + "Raw": "a <&> b", + "Bytes": []byte("hello"), + } + for _, c := range formatterTests { + tmpl, err := Parse(c.in, formatters) + if err != nil { + if c.err == "" { + t.Error("unexpected parse error:", err) + continue + } + if strings.Index(err.String(), c.err) < 0 { + t.Errorf("unexpected error: expected %q, got %q", c.err, err.String()) + continue + } + } else { + if c.err != "" { + t.Errorf("For %q, expected error, got none.", c.in) + continue + } + buf := bytes.NewBuffer(nil) + err = tmpl.Execute(buf, data) + if err != nil { + t.Error("unexpected Execute error: ", err) + continue + } + actual := buf.String() + if actual != c.out { + t.Errorf("for %q: expected %q but got %q.", c.in, c.out, actual) + } + } + } +} diff --git a/src/pkg/os/user/Makefile b/src/pkg/os/user/Makefile index 731f7999a..aabb54995 100644 --- a/src/pkg/os/user/Makefile +++ b/src/pkg/os/user/Makefile @@ -8,7 +8,7 @@ TARG=os/user GOFILES=\ user.go\ -ifneq ($(GOARCH),arm) +ifeq ($(CGO_ENABLED),1) CGOFILES_linux=\ lookup_unix.go CGOFILES_freebsd=\ diff --git a/src/pkg/os/user/lookup_unix.go b/src/pkg/os/user/lookup_unix.go index 678de802b..1b2c9e8c9 100644 --- a/src/pkg/os/user/lookup_unix.go +++ b/src/pkg/os/user/lookup_unix.go @@ -25,6 +25,10 @@ static int mygetpwuid_r(int uid, struct passwd *pwd, */ import "C" +func init() { + implemented = true +} + // Lookup looks up a user by username. If the user cannot be found, // the returned error is of type UnknownUserError. func Lookup(username string) (*User, os.Error) { diff --git a/src/pkg/os/user/user.go b/src/pkg/os/user/user.go index dd009211d..f71e11d8b 100644 --- a/src/pkg/os/user/user.go +++ b/src/pkg/os/user/user.go @@ -9,6 +9,8 @@ import ( "strconv" ) +var implemented = false // set to true by lookup_unix.go's init + // User represents a user account. type User struct { Uid int // user id diff --git a/src/pkg/os/user/user_test.go b/src/pkg/os/user/user_test.go index ee917b57a..59f15e4c6 100644 --- a/src/pkg/os/user/user_test.go +++ b/src/pkg/os/user/user_test.go @@ -13,8 +13,8 @@ import ( ) func skip(t *testing.T) bool { - if runtime.GOARCH == "arm" { - t.Logf("user: cgo not implemented on arm; skipping tests") + if !implemented { + t.Logf("user: not implemented; skipping tests") return true } diff --git a/src/pkg/path/filepath/Makefile b/src/pkg/path/filepath/Makefile index bc26a7d6a..af250ab33 100644 --- a/src/pkg/path/filepath/Makefile +++ b/src/pkg/path/filepath/Makefile @@ -18,6 +18,9 @@ GOFILES_darwin=\ GOFILES_linux=\ path_unix.go +GOFILES_openbsd=\ + path_unix.go + GOFILES_plan9=\ path_plan9.go diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index 82985c575..d3c510ac2 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -860,6 +860,9 @@ func (v Value) Interface() interface{} { } func (iv internalValue) Interface() interface{} { + if iv.kind == 0 { + panic(&ValueError{"reflect.Value.Interface", iv.kind}) + } if iv.method { panic("reflect.Value.Interface: cannot create interface value for method with bound receiver") } @@ -974,7 +977,7 @@ func (v Value) MapIndex(key Value) Value { flag := (iv.flag | ikey.flag) & flagRO elemType := typ.Elem() - elemWord, ok := mapaccess(iv.word, ikey.word) + elemWord, ok := mapaccess(typ.runtimeType(), iv.word, ikey.word) if !ok { return Value{} } @@ -996,7 +999,7 @@ func (v Value) MapKeys() []Value { if m != 0 { mlen = maplen(m) } - it := mapiterinit(m) + it := mapiterinit(iv.typ.runtimeType(), m) a := make([]Value, mlen) var i int for i = 0; i < len(a); i++ { @@ -1159,7 +1162,7 @@ func (iv internalValue) recv(nb bool) (val Value, ok bool) { if ch == 0 { panic("recv on nil channel") } - valWord, selected, ok := chanrecv(ch, nb) + valWord, selected, ok := chanrecv(iv.typ.runtimeType(), ch, nb) if selected { val = valueFromIword(0, t.Elem(), valWord) } @@ -1189,7 +1192,7 @@ func (iv internalValue) send(x Value, nb bool) (selected bool) { if ch == 0 { panic("send on nil channel") } - return chansend(ch, ix.word, nb) + return chansend(iv.typ.runtimeType(), ch, ix.word, nb) } // Set assigns x to the value v. @@ -1306,7 +1309,7 @@ func (v Value) SetMapIndex(key, val Value) { ival = convertForAssignment("reflect.Value.SetMapIndex", nil, iv.typ.Elem(), ival) } - mapassign(iv.word, ikey.word, ival.word, ival.kind != Invalid) + mapassign(iv.typ.runtimeType(), iv.word, ikey.word, ival.word, ival.kind != Invalid) } // SetUint sets v's underlying value to x. @@ -1717,14 +1720,14 @@ func convertForAssignment(what string, addr unsafe.Pointer, dst Type, iv interna func chancap(ch iword) int32 func chanclose(ch iword) func chanlen(ch iword) int32 -func chanrecv(ch iword, nb bool) (val iword, selected, received bool) -func chansend(ch iword, val iword, nb bool) bool +func chanrecv(t *runtime.Type, ch iword, nb bool) (val iword, selected, received bool) +func chansend(t *runtime.Type, ch iword, val iword, nb bool) bool func makechan(typ *runtime.Type, size uint32) (ch iword) func makemap(t *runtime.Type) iword -func mapaccess(m iword, key iword) (val iword, ok bool) -func mapassign(m iword, key, val iword, ok bool) -func mapiterinit(m iword) *byte +func mapaccess(t *runtime.Type, m iword, key iword) (val iword, ok bool) +func mapassign(t *runtime.Type, m iword, key, val iword, ok bool) +func mapiterinit(t *runtime.Type, m iword) *byte func mapiterkey(it *byte) (key iword, ok bool) func mapiternext(it *byte) func maplen(m iword) int32 diff --git a/src/pkg/rpc/debug.go b/src/pkg/rpc/debug.go index ae76a4586..7e3e6f6e5 100644 --- a/src/pkg/rpc/debug.go +++ b/src/pkg/rpc/debug.go @@ -10,10 +10,10 @@ package rpc */ import ( - "exp/template" "fmt" "http" "sort" + "template" ) const debugText = ` @@ -70,7 +70,7 @@ func (server debugHTTP) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Build a sorted version of the data. var services = make(serviceArray, len(server.serviceMap)) i := 0 - server.Lock() + server.mu.Lock() for sname, service := range server.serviceMap { services[i] = debugService{service, sname, make(methodArray, len(service.method))} j := 0 @@ -81,7 +81,7 @@ func (server debugHTTP) ServeHTTP(w http.ResponseWriter, req *http.Request) { sort.Sort(services[i].Method) i++ } - server.Unlock() + server.mu.Unlock() sort.Sort(services) err := debug.Execute(w, services) if err != nil { diff --git a/src/pkg/rpc/server.go b/src/pkg/rpc/server.go index 86767abea..745074428 100644 --- a/src/pkg/rpc/server.go +++ b/src/pkg/rpc/server.go @@ -174,7 +174,7 @@ type Response struct { // Server represents an RPC Server. type Server struct { - sync.Mutex // protects the serviceMap + mu sync.Mutex // protects the serviceMap serviceMap map[string]*service reqLock sync.Mutex // protects freeReq freeReq *Request @@ -226,8 +226,8 @@ func (server *Server) RegisterName(name string, rcvr interface{}) os.Error { } func (server *Server) register(rcvr interface{}, name string, useName bool) os.Error { - server.Lock() - defer server.Unlock() + server.mu.Lock() + defer server.mu.Unlock() if server.serviceMap == nil { server.serviceMap = make(map[string]*service) } @@ -394,7 +394,7 @@ func (server *Server) ServeConn(conn io.ReadWriteCloser) { func (server *Server) ServeCodec(codec ServerCodec) { sending := new(sync.Mutex) for { - req, service, mtype, err := server.readRequest(codec) + service, mtype, req, argv, replyv, err := server.readRequest(codec) if err != nil { if err != os.EOF { log.Println("rpc:", err) @@ -402,9 +402,6 @@ func (server *Server) ServeCodec(codec ServerCodec) { if err == os.EOF || err == io.ErrUnexpectedEOF { break } - // discard body - codec.ReadRequestBody(nil) - // send a response if we actually managed to read a header. if req != nil { server.sendResponse(sending, req, invalidRequest, codec, err.String()) @@ -412,35 +409,29 @@ func (server *Server) ServeCodec(codec ServerCodec) { } continue } + go service.call(server, sending, mtype, req, argv, replyv, codec) + } + codec.Close() +} - // Decode the argument value. - var argv reflect.Value - argIsValue := false // if true, need to indirect before calling. - if mtype.ArgType.Kind() == reflect.Ptr { - argv = reflect.New(mtype.ArgType.Elem()) - } else { - argv = reflect.New(mtype.ArgType) - argIsValue = true - } - // argv guaranteed to be a pointer now. - replyv := reflect.New(mtype.ReplyType.Elem()) - err = codec.ReadRequestBody(argv.Interface()) - if err != nil { - if err == os.EOF || err == io.ErrUnexpectedEOF { - if err == io.ErrUnexpectedEOF { - log.Println("rpc:", err) - } - break - } - server.sendResponse(sending, req, replyv.Interface(), codec, err.String()) - continue +// ServeRequest is like ServeCodec but synchronously serves a single request. +// It does not close the codec upon completion. +func (server *Server) ServeRequest(codec ServerCodec) os.Error { + sending := new(sync.Mutex) + service, mtype, req, argv, replyv, err := server.readRequest(codec) + if err != nil { + if err == os.EOF || err == io.ErrUnexpectedEOF { + return err } - if argIsValue { - argv = argv.Elem() + // send a response if we actually managed to read a header. + if req != nil { + server.sendResponse(sending, req, invalidRequest, codec, err.String()) + server.freeRequest(req) } - go service.call(server, sending, mtype, req, argv, replyv, codec) + return err } - codec.Close() + service.call(server, sending, mtype, req, argv, replyv, codec) + return nil } func (server *Server) getRequest() *Request { @@ -483,7 +474,38 @@ func (server *Server) freeResponse(resp *Response) { server.respLock.Unlock() } -func (server *Server) readRequest(codec ServerCodec) (req *Request, service *service, mtype *methodType, err os.Error) { +func (server *Server) readRequest(codec ServerCodec) (service *service, mtype *methodType, req *Request, argv, replyv reflect.Value, err os.Error) { + service, mtype, req, err = server.readRequestHeader(codec) + if err != nil { + if err == os.EOF || err == io.ErrUnexpectedEOF { + return + } + // discard body + codec.ReadRequestBody(nil) + return + } + + // Decode the argument value. + argIsValue := false // if true, need to indirect before calling. + if mtype.ArgType.Kind() == reflect.Ptr { + argv = reflect.New(mtype.ArgType.Elem()) + } else { + argv = reflect.New(mtype.ArgType) + argIsValue = true + } + // argv guaranteed to be a pointer now. + if err = codec.ReadRequestBody(argv.Interface()); err != nil { + return + } + if argIsValue { + argv = argv.Elem() + } + + replyv = reflect.New(mtype.ReplyType.Elem()) + return +} + +func (server *Server) readRequestHeader(codec ServerCodec) (service *service, mtype *methodType, req *Request, err os.Error) { // Grab the request header. req = server.getRequest() err = codec.ReadRequestHeader(req) @@ -502,9 +524,9 @@ func (server *Server) readRequest(codec ServerCodec) (req *Request, service *ser return } // Look up the request. - server.Lock() + server.mu.Lock() service = server.serviceMap[serviceMethod[0]] - server.Unlock() + server.mu.Unlock() if service == nil { err = os.NewError("rpc: can't find service " + req.ServiceMethod) return @@ -568,6 +590,12 @@ func ServeCodec(codec ServerCodec) { DefaultServer.ServeCodec(codec) } +// ServeRequest is like ServeCodec but synchronously serves a single request. +// It does not close the codec upon completion. +func ServeRequest(codec ServerCodec) os.Error { + return DefaultServer.ServeRequest(codec) +} + // Accept accepts connections on the listener and serves requests // to DefaultServer for each incoming connection. // Accept blocks; the caller typically invokes it in a go statement. diff --git a/src/pkg/rpc/server_test.go b/src/pkg/rpc/server_test.go index 459dd59d6..e7bbfbe97 100644 --- a/src/pkg/rpc/server_test.go +++ b/src/pkg/rpc/server_test.go @@ -7,6 +7,7 @@ package rpc import ( "fmt" "http/httptest" + "io" "log" "net" "os" @@ -18,6 +19,7 @@ import ( ) var ( + newServer *Server serverAddr, newServerAddr string httpServerAddr string once, newOnce, httpOnce sync.Once @@ -93,15 +95,15 @@ func startServer() { } func startNewServer() { - s := NewServer() - s.Register(new(Arith)) + newServer = NewServer() + newServer.Register(new(Arith)) var l net.Listener l, newServerAddr = listenTCP() log.Println("NewServer test RPC server listening on", newServerAddr) go Accept(l) - s.HandleHTTP(newHttpPath, "/bar") + newServer.HandleHTTP(newHttpPath, "/bar") httpOnce.Do(startHttpServer) } @@ -264,6 +266,85 @@ func testHTTPRPC(t *testing.T, path string) { } } +// CodecEmulator provides a client-like api and a ServerCodec interface. +// Can be used to test ServeRequest. +type CodecEmulator struct { + server *Server + serviceMethod string + args *Args + reply *Reply + err os.Error +} + +func (codec *CodecEmulator) Call(serviceMethod string, args *Args, reply *Reply) os.Error { + codec.serviceMethod = serviceMethod + codec.args = args + codec.reply = reply + codec.err = nil + var serverError os.Error + if codec.server == nil { + serverError = ServeRequest(codec) + } else { + serverError = codec.server.ServeRequest(codec) + } + if codec.err == nil && serverError != nil { + codec.err = serverError + } + return codec.err +} + +func (codec *CodecEmulator) ReadRequestHeader(req *Request) os.Error { + req.ServiceMethod = codec.serviceMethod + req.Seq = 0 + return nil +} + +func (codec *CodecEmulator) ReadRequestBody(argv interface{}) os.Error { + if codec.args == nil { + return io.ErrUnexpectedEOF + } + *(argv.(*Args)) = *codec.args + return nil +} + +func (codec *CodecEmulator) WriteResponse(resp *Response, reply interface{}) os.Error { + if resp.Error != "" { + codec.err = os.NewError(resp.Error) + } + *codec.reply = *(reply.(*Reply)) + return nil +} + +func (codec *CodecEmulator) Close() os.Error { + return nil +} + +func TestServeRequest(t *testing.T) { + once.Do(startServer) + testServeRequest(t, nil) + newOnce.Do(startNewServer) + testServeRequest(t, newServer) +} + +func testServeRequest(t *testing.T, server *Server) { + client := CodecEmulator{server: server} + + args := &Args{7, 8} + reply := new(Reply) + err := client.Call("Arith.Add", args, reply) + if err != nil { + t.Errorf("Add: expected no error but got string %q", err.String()) + } + if reply.C != args.A+args.B { + t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B) + } + + err = client.Call("Arith.Add", nil, reply) + if err == nil { + t.Errorf("expected error calling Arith.Add with nil arg") + } +} + type ReplyNotPointer int type ArgNotPublic int type ReplyNotPublic int diff --git a/src/pkg/runtime/cgo/Makefile b/src/pkg/runtime/cgo/Makefile index e7a2fa7c6..766794797 100644 --- a/src/pkg/runtime/cgo/Makefile +++ b/src/pkg/runtime/cgo/Makefile @@ -4,29 +4,12 @@ include ../../../Make.inc -ENABLED:=1 - -ifeq ($(GOARCH),arm) -ENABLED:=0 -endif - -ifeq ($(GOOS),plan9) -ENABLED:=0 -endif -ifeq ($(GOOS),openbsd) -ENABLED:=0 -endif - -ifeq ($(DISABLE_CGO),1) -ENABLED:=0 -endif - TARG=runtime/cgo GOFILES=\ cgo.go\ -ifeq ($(ENABLED),1) +ifeq ($(CGO_ENABLED),1) # Unwarranted chumminess with Make.pkg's cgo rules. # Do not try this at home. @@ -59,7 +42,7 @@ endif include ../../../Make.pkg -ifeq ($(ENABLED),1) +ifeq ($(CGO_ENABLED),1) _cgo_defun.c: echo >$@ diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c index 65feacb78..ef5342353 100644 --- a/src/pkg/runtime/chan.c +++ b/src/pkg/runtime/chan.c @@ -81,10 +81,13 @@ static void enqueue(WaitQ*, SudoG*); static void destroychan(Hchan*); Hchan* -runtime·makechan_c(Type *elem, int64 hint) +runtime·makechan_c(ChanType *t, int64 hint) { Hchan *c; int32 n; + Type *elem; + + elem = t->elem; if(hint < 0 || (int32)hint != hint || (elem->size > 0 && hint > ((uintptr)-1) / elem->size)) runtime·panicstring("makechan: size out of range"); @@ -121,7 +124,7 @@ runtime·makechan_c(Type *elem, int64 hint) void reflect·makechan(ChanType *t, uint32 size, Hchan *c) { - c = runtime·makechan_c(t->elem, size); + c = runtime·makechan_c(t, size); FLUSH(&c); } @@ -132,11 +135,11 @@ destroychan(Hchan *c) } -// makechan(elem *Type, hint int64) (hchan *chan any); +// makechan(t *ChanType, hint int64) (hchan *chan any); void -runtime·makechan(Type *elem, int64 hint, Hchan *ret) +runtime·makechan(ChanType *t, int64 hint, Hchan *ret) { - ret = runtime·makechan_c(elem, hint); + ret = runtime·makechan_c(t, hint); FLUSH(&ret); } @@ -155,14 +158,22 @@ runtime·makechan(Type *elem, int64 hint, Hchan *ret) * the operation; we'll see that it's now closed. */ void -runtime·chansend(Hchan *c, byte *ep, bool *pres) +runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres) { SudoG *sg; SudoG mysg; G* gp; - if(c == nil) - runtime·panicstring("send to nil channel"); + if(c == nil) { + USED(t); + if(pres != nil) { + *pres = false; + return; + } + g->status = Gwaiting; + runtime·gosched(); + return; // not reached + } if(runtime·gcwaiting) runtime·gosched(); @@ -263,21 +274,29 @@ closed: void -runtime·chanrecv(Hchan* c, byte *ep, bool *selected, bool *received) +runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *received) { SudoG *sg; SudoG mysg; G *gp; - if(c == nil) - runtime·panicstring("receive from nil channel"); - if(runtime·gcwaiting) runtime·gosched(); if(debug) runtime·printf("chanrecv: chan=%p\n", c); + if(c == nil) { + USED(t); + if(selected != nil) { + *selected = false; + return; + } + g->status = Gwaiting; + runtime·gosched(); + return; // not reached + } + runtime·lock(c); if(c->dataqsiz > 0) goto asynch; @@ -385,50 +404,29 @@ closed: // chansend1(hchan *chan any, elem any); #pragma textflag 7 void -runtime·chansend1(Hchan* c, ...) +runtime·chansend1(ChanType *t, Hchan* c, ...) { - int32 o; - byte *ae; - - if(c == nil) - runtime·panicstring("send to nil channel"); - - o = runtime·rnd(sizeof(c), c->elemalign); - ae = (byte*)&c + o; - runtime·chansend(c, ae, nil); + runtime·chansend(t, c, (byte*)(&c+1), nil); } // chanrecv1(hchan *chan any) (elem any); #pragma textflag 7 void -runtime·chanrecv1(Hchan* c, ...) +runtime·chanrecv1(ChanType *t, Hchan* c, ...) { - int32 o; - byte *ae; - - o = runtime·rnd(sizeof(c), Structrnd); - ae = (byte*)&c + o; - - runtime·chanrecv(c, ae, nil, nil); + runtime·chanrecv(t, c, (byte*)(&c+1), nil, nil); } // chanrecv2(hchan *chan any) (elem any, received bool); #pragma textflag 7 void -runtime·chanrecv2(Hchan* c, ...) +runtime·chanrecv2(ChanType *t, Hchan* c, ...) { - int32 o; - byte *ae, *ac; - - if(c == nil) - runtime·panicstring("receive from nil channel"); - - o = runtime·rnd(sizeof(c), Structrnd); - ae = (byte*)&c + o; - o += c->elemsize; - ac = (byte*)&c + o; + byte *ae, *ap; - runtime·chanrecv(c, ae, nil, ac); + ae = (byte*)(&c+1); + ap = ae + t->elem->size; + runtime·chanrecv(t, c, ae, nil, ap); } // func selectnbsend(c chan any, elem any) bool @@ -444,7 +442,7 @@ runtime·chanrecv2(Hchan* c, ...) // // as // -// if c != nil && selectnbsend(c, v) { +// if selectnbsend(c, v) { // ... foo // } else { // ... bar @@ -452,17 +450,13 @@ runtime·chanrecv2(Hchan* c, ...) // #pragma textflag 7 void -runtime·selectnbsend(Hchan *c, ...) +runtime·selectnbsend(ChanType *t, Hchan *c, ...) { - int32 o; byte *ae, *ap; - o = runtime·rnd(sizeof(c), c->elemalign); - ae = (byte*)&c + o; - o = runtime·rnd(o+c->elemsize, Structrnd); - ap = (byte*)&c + o; - - runtime·chansend(c, ae, ap); + ae = (byte*)(&c + 1); + ap = ae + runtime·rnd(t->elem->size, Structrnd); + runtime·chansend(t, c, ae, ap); } // func selectnbrecv(elem *any, c chan any) bool @@ -478,7 +472,7 @@ runtime·selectnbsend(Hchan *c, ...) // // as // -// if c != nil && selectnbrecv(&v, c) { +// if selectnbrecv(&v, c) { // ... foo // } else { // ... bar @@ -486,9 +480,9 @@ runtime·selectnbsend(Hchan *c, ...) // #pragma textflag 7 void -runtime·selectnbrecv(byte *v, Hchan *c, bool selected) +runtime·selectnbrecv(ChanType *t, byte *v, Hchan *c, bool selected) { - runtime·chanrecv(c, v, &selected, nil); + runtime·chanrecv(t, c, v, &selected, nil); } // func selectnbrecv2(elem *any, ok *bool, c chan any) bool @@ -512,9 +506,9 @@ runtime·selectnbrecv(byte *v, Hchan *c, bool selected) // #pragma textflag 7 void -runtime·selectnbrecv2(byte *v, bool *received, Hchan *c, bool selected) +runtime·selectnbrecv2(ChanType *t, byte *v, bool *received, Hchan *c, bool selected) { - runtime·chanrecv(c, v, &selected, received); + runtime·chanrecv(t, c, v, &selected, received); } // For reflect: @@ -525,14 +519,11 @@ runtime·selectnbrecv2(byte *v, bool *received, Hchan *c, bool selected) // The "uintptr selected" is really "bool selected" but saying // uintptr gets us the right alignment for the output parameter block. void -reflect·chansend(Hchan *c, uintptr val, bool nb, uintptr selected) +reflect·chansend(ChanType *t, Hchan *c, uintptr val, bool nb, uintptr selected) { bool *sp; byte *vp; - if(c == nil) - runtime·panicstring("send to nil channel"); - if(nb) { selected = false; sp = (bool*)&selected; @@ -541,11 +532,11 @@ reflect·chansend(Hchan *c, uintptr val, bool nb, uintptr selected) FLUSH(&selected); sp = nil; } - if(c->elemsize <= sizeof(val)) + if(t->elem->size <= sizeof(val)) vp = (byte*)&val; else vp = (byte*)val; - runtime·chansend(c, vp, sp); + runtime·chansend(t, c, vp, sp); } // For reflect: @@ -553,13 +544,10 @@ reflect·chansend(Hchan *c, uintptr val, bool nb, uintptr selected) // where an iword is the same word an interface value would use: // the actual data if it fits, or else a pointer to the data. void -reflect·chanrecv(Hchan *c, bool nb, uintptr val, bool selected, bool received) +reflect·chanrecv(ChanType *t, Hchan *c, bool nb, uintptr val, bool selected, bool received) { byte *vp; bool *sp; - - if(c == nil) - runtime·panicstring("receive from nil channel"); if(nb) { selected = false; @@ -571,15 +559,15 @@ reflect·chanrecv(Hchan *c, bool nb, uintptr val, bool selected, bool received) } received = false; FLUSH(&received); - if(c->elemsize <= sizeof(val)) { + if(t->elem->size <= sizeof(val)) { val = 0; vp = (byte*)&val; } else { - vp = runtime·mal(c->elemsize); + vp = runtime·mal(t->elem->size); val = (uintptr)vp; FLUSH(&val); } - runtime·chanrecv(c, vp, sp, &received); + runtime·chanrecv(t, c, vp, sp, &received); } static void newselect(int32, Select**); @@ -904,7 +892,8 @@ loop: // pass 2 - enqueue on all chans for(i=0; incase; i++) { - cas = &sel->scase[i]; + o = sel->pollorder[i]; + cas = &sel->scase[o]; c = cas->chan; sg = &cas->sg; sg->g = g; diff --git a/src/pkg/runtime/chan_test.go b/src/pkg/runtime/chan_test.go index 71c9e2fd7..46ddfd7e8 100644 --- a/src/pkg/runtime/chan_test.go +++ b/src/pkg/runtime/chan_test.go @@ -6,6 +6,7 @@ package runtime_test import ( "runtime" + "sync" "sync/atomic" "testing" ) @@ -26,6 +27,38 @@ func TestChanSendInterface(t *testing.T) { } } +func TestPseudoRandomSend(t *testing.T) { + n := 100 + c := make(chan int) + l := make([]int, n) + var m sync.Mutex + m.Lock() + go func() { + for i := 0; i < n; i++ { + runtime.Gosched() + l[i] = <-c + } + m.Unlock() + }() + for i := 0; i < n; i++ { + select { + case c <- 0: + case c <- 1: + } + } + m.Lock() // wait + n0 := 0 + n1 := 0 + for _, i := range l { + n0 += (i + 1) % 2 + n1 += i + if n0 > n/10 && n1 > n/10 { + return + } + } + t.Errorf("Want pseudo random, got %d zeros and %d ones", n0, n1) +} + func BenchmarkSelectUncontended(b *testing.B) { const CallsPerSched = 1000 procs := runtime.GOMAXPROCS(-1) diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c index 179a56375..0c0e3e4a2 100644 --- a/src/pkg/runtime/hashmap.c +++ b/src/pkg/runtime/hashmap.c @@ -690,13 +690,17 @@ hash_indirect(Hmap *h, void *p) static int32 debug = 0; -// makemap(key, val *Type, hint uint32) (hmap *map[any]any); +// makemap(typ *Type, hint uint32) (hmap *map[any]any); Hmap* -runtime·makemap_c(Type *key, Type *val, int64 hint) +runtime·makemap_c(MapType *typ, int64 hint) { Hmap *h; int32 keyalg, valalg, keysize, valsize, valsize_in_hash; void (*data_del)(uint32, void*, void*); + Type *key, *val; + + key = typ->key; + val = typ->elem; if(hint < 0 || (int32)hint != hint) runtime·panicstring("makemap: size out of range"); @@ -770,9 +774,9 @@ runtime·makemap_c(Type *key, Type *val, int64 hint) // makemap(key, val *Type, hint int64) (hmap *map[any]any); void -runtime·makemap(Type *key, Type *val, int64 hint, Hmap *ret) +runtime·makemap(MapType *typ, int64 hint, Hmap *ret) { - ret = runtime·makemap_c(key, val, hint); + ret = runtime·makemap_c(typ, hint); FLUSH(&ret); } @@ -781,17 +785,22 @@ runtime·makemap(Type *key, Type *val, int64 hint, Hmap *ret) void reflect·makemap(MapType *t, Hmap *ret) { - ret = runtime·makemap_c(t->key, t->elem, 0); + ret = runtime·makemap_c(t, 0); FLUSH(&ret); } void -runtime·mapaccess(Hmap *h, byte *ak, byte *av, bool *pres) +runtime·mapaccess(MapType *t, Hmap *h, byte *ak, byte *av, bool *pres) { byte *res; + Type *elem; - if(h == nil) - runtime·panicstring("lookup in nil map"); + if(h == nil) { + elem = t->elem; + runtime·algarray[elem->alg].copy(elem->size, av, nil); + *pres = false; + return; + } if(runtime·gcwaiting) runtime·gosched(); @@ -809,18 +818,20 @@ runtime·mapaccess(Hmap *h, byte *ak, byte *av, bool *pres) // mapaccess1(hmap *map[any]any, key any) (val any); #pragma textflag 7 void -runtime·mapaccess1(Hmap *h, ...) +runtime·mapaccess1(MapType *t, Hmap *h, ...) { byte *ak, *av; bool pres; - if(h == nil) - runtime·panicstring("lookup in nil map"); - - ak = (byte*)&h + h->ko1; - av = (byte*)&h + h->vo1; + if(h == nil) { + ak = (byte*)(&h + 1); + av = ak + runtime·rnd(t->key->size, Structrnd); + } else { + ak = (byte*)&h + h->ko1; + av = (byte*)&h + h->vo1; + } - runtime·mapaccess(h, ak, av, &pres); + runtime·mapaccess(t, h, ak, av, &pres); if(debug) { runtime·prints("runtime.mapaccess1: map="); @@ -838,18 +849,21 @@ runtime·mapaccess1(Hmap *h, ...) // mapaccess2(hmap *map[any]any, key any) (val any, pres bool); #pragma textflag 7 void -runtime·mapaccess2(Hmap *h, ...) +runtime·mapaccess2(MapType *t, Hmap *h, ...) { byte *ak, *av, *ap; - if(h == nil) - runtime·panicstring("lookup in nil map"); - - ak = (byte*)&h + h->ko1; - av = (byte*)&h + h->vo1; - ap = (byte*)&h + h->po1; + if(h == nil) { + ak = (byte*)(&h + 1); + av = ak + runtime·rnd(t->key->size, Structrnd); + ap = av + t->elem->size; + } else { + ak = (byte*)&h + h->ko1; + av = (byte*)&h + h->vo1; + ap = (byte*)&h + h->po1; + } - runtime·mapaccess(h, ak, av, ap); + runtime·mapaccess(t, h, ak, av, ap); if(debug) { runtime·prints("runtime.mapaccess2: map="); @@ -865,39 +879,39 @@ runtime·mapaccess2(Hmap *h, ...) } // For reflect: -// func mapaccess(h map, key iword) (val iword, pres bool) +// func mapaccess(t type, h map, key iword) (val iword, pres bool) // where an iword is the same word an interface value would use: // the actual data if it fits, or else a pointer to the data. void -reflect·mapaccess(Hmap *h, uintptr key, uintptr val, bool pres) +reflect·mapaccess(MapType *t, Hmap *h, uintptr key, uintptr val, bool pres) { byte *ak, *av; - if(h == nil) - runtime·panicstring("lookup in nil map"); - if(h->keysize <= sizeof(key)) + if(t->key->size <= sizeof(key)) ak = (byte*)&key; else ak = (byte*)key; val = 0; pres = false; - if(h->valsize <= sizeof(val)) + if(t->elem->size <= sizeof(val)) av = (byte*)&val; else { - av = runtime·mal(h->valsize); + av = runtime·mal(t->elem->size); val = (uintptr)av; } - runtime·mapaccess(h, ak, av, &pres); + runtime·mapaccess(t, h, ak, av, &pres); FLUSH(&val); FLUSH(&pres); } void -runtime·mapassign(Hmap *h, byte *ak, byte *av) +runtime·mapassign(MapType *t, Hmap *h, byte *ak, byte *av) { byte *res; int32 hit; + USED(t); + if(h == nil) runtime·panicstring("assignment to entry in nil map"); @@ -931,10 +945,10 @@ runtime·mapassign(Hmap *h, byte *ak, byte *av) } } -// mapassign1(hmap *map[any]any, key any, val any); +// mapassign1(mapType *type, hmap *map[any]any, key any, val any); #pragma textflag 7 void -runtime·mapassign1(Hmap *h, ...) +runtime·mapassign1(MapType *t, Hmap *h, ...) { byte *ak, *av; @@ -944,13 +958,13 @@ runtime·mapassign1(Hmap *h, ...) ak = (byte*)&h + h->ko2; av = (byte*)&h + h->vo2; - runtime·mapassign(h, ak, av); + runtime·mapassign(t, h, ak, av); } -// mapassign2(hmap *map[any]any, key any, val any, pres bool); +// mapassign2(mapType *type, hmap *map[any]any, key any, val any, pres bool); #pragma textflag 7 void -runtime·mapassign2(Hmap *h, ...) +runtime·mapassign2(MapType *t, Hmap *h, ...) { byte *ak, *av, *ap; @@ -964,7 +978,7 @@ runtime·mapassign2(Hmap *h, ...) if(*ap == false) av = nil; // delete - runtime·mapassign(h, ak, av); + runtime·mapassign(t, h, ak, av); if(debug) { runtime·prints("mapassign2: map="); @@ -976,16 +990,16 @@ runtime·mapassign2(Hmap *h, ...) } // For reflect: -// func mapassign(h map, key, val iword, pres bool) +// func mapassign(t type h map, key, val iword, pres bool) // where an iword is the same word an interface value would use: // the actual data if it fits, or else a pointer to the data. void -reflect·mapassign(Hmap *h, uintptr key, uintptr val, bool pres) +reflect·mapassign(MapType *t, Hmap *h, uintptr key, uintptr val, bool pres) { byte *ak, *av; if(h == nil) - runtime·panicstring("lookup in nil map"); + runtime·panicstring("assignment to entry in nil map"); if(h->keysize <= sizeof(key)) ak = (byte*)&key; else @@ -996,12 +1010,12 @@ reflect·mapassign(Hmap *h, uintptr key, uintptr val, bool pres) av = (byte*)val; if(!pres) av = nil; - runtime·mapassign(h, ak, av); + runtime·mapassign(t, h, ak, av); } -// mapiterinit(hmap *map[any]any, hiter *any); +// mapiterinit(mapType *type, hmap *map[any]any, hiter *any); void -runtime·mapiterinit(Hmap *h, struct hash_iter *it) +runtime·mapiterinit(MapType*, Hmap *h, struct hash_iter *it) { if(h == nil) { it->data = nil; @@ -1023,11 +1037,11 @@ runtime·mapiterinit(Hmap *h, struct hash_iter *it) // For reflect: // func mapiterinit(h map) (it iter) void -reflect·mapiterinit(Hmap *h, struct hash_iter *it) +reflect·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it) { it = runtime·mal(sizeof *it); FLUSH(&it); - runtime·mapiterinit(h, it); + runtime·mapiterinit(t, h, it); } // mapiternext(hiter *any); diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c index 6325aadc6..78ea2aa2b 100644 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -737,11 +737,11 @@ runtime·markallocated(void *v, uintptr n, bool noptr) bits = (obits & ~(bitMask< 1: use atomic op + // more than one goroutine is potentially running: use atomic op if(runtime·casp((void**)b, (void*)obits, (void*)bits)) break; } @@ -767,11 +767,11 @@ runtime·markfreed(void *v, uintptr n) for(;;) { obits = *b; bits = (obits & ~(bitMask< 1: use atomic op + // more than one goroutine is potentially running: use atomic op if(runtime·casp((void**)b, (void*)obits, (void*)bits)) break; } @@ -878,11 +878,11 @@ runtime·setblockspecial(void *v) for(;;) { obits = *b; bits = obits | (bitSpecial< 1: use atomic op + // more than one goroutine is potentially running: use atomic op if(runtime·casp((void**)b, (void*)obits, (void*)bits)) break; } diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c index 7a8159100..5f396b49f 100644 --- a/src/pkg/runtime/proc.c +++ b/src/pkg/runtime/proc.c @@ -126,6 +126,7 @@ enum { Sched runtime·sched; int32 runtime·gomaxprocs; +bool runtime·singleproc; // An m that is waiting for notewakeup(&m->havenextg). This may be // only be accessed while the scheduler lock is held. This is used to @@ -199,6 +200,7 @@ runtime·schedinit(void) runtime·gomaxprocs = n; } setmcpumax(runtime·gomaxprocs); + runtime·singleproc = runtime·gomaxprocs == 1; runtime·sched.predawn = 1; m->nomemprof--; @@ -585,6 +587,7 @@ runtime·stoptheworld(void) runtime·notesleep(&runtime·sched.stopped); schedlock(); } + runtime·singleproc = runtime·gomaxprocs == 1; schedunlock(); } @@ -1416,6 +1419,8 @@ runtime·gomaxprocsfunc(int32 n) if(n > maxgomaxprocs) n = maxgomaxprocs; runtime·gomaxprocs = n; + if(runtime·gomaxprocs > 1) + runtime·singleproc = false; if(runtime·gcwaiting != 0) { if(atomic_mcpumax(runtime·sched.atomic) != 1) runtime·throw("invalid mcpumax during gc"); diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 8c5403f44..526a320ea 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -62,6 +62,8 @@ typedef struct Iface Iface; typedef struct Itab Itab; typedef struct Eface Eface; typedef struct Type Type; +typedef struct ChanType ChanType; +typedef struct MapType MapType; typedef struct Defer Defer; typedef struct Panic Panic; typedef struct Hmap Hmap; @@ -248,12 +250,9 @@ struct M uint32 freghi[16]; // D[i] msb and F[i+16] uint32 fflag; // floating point compare flags #ifdef __WINDOWS__ - void* sehframe; - #ifdef _64BIT void* gostack; #endif - #endif }; @@ -386,6 +385,7 @@ extern String runtime·emptystring; G* runtime·allg; M* runtime·allm; extern int32 runtime·gomaxprocs; +extern bool runtime·singleproc; extern uint32 runtime·panicking; extern int32 runtime·gcwaiting; // gc is waiting to run int8* runtime·goos; @@ -618,16 +618,16 @@ int32 runtime·gomaxprocsfunc(int32 n); void runtime·procyield(uint32); void runtime·osyield(void); -void runtime·mapassign(Hmap*, byte*, byte*); -void runtime·mapaccess(Hmap*, byte*, byte*, bool*); +void runtime·mapassign(MapType*, Hmap*, byte*, byte*); +void runtime·mapaccess(MapType*, Hmap*, byte*, byte*, bool*); void runtime·mapiternext(struct hash_iter*); bool runtime·mapiterkey(struct hash_iter*, void*); void runtime·mapiterkeyvalue(struct hash_iter*, void*, void*); -Hmap* runtime·makemap_c(Type*, Type*, int64); +Hmap* runtime·makemap_c(MapType*, int64); -Hchan* runtime·makechan_c(Type*, int64); -void runtime·chansend(Hchan*, void*, bool*); -void runtime·chanrecv(Hchan*, void*, bool*, bool*); +Hchan* runtime·makechan_c(ChanType*, int64); +void runtime·chansend(ChanType*, Hchan*, void*, bool*); +void runtime·chanrecv(ChanType*, Hchan*, void*, bool*, bool*); int32 runtime·chanlen(Hchan*); int32 runtime·chancap(Hchan*); diff --git a/src/pkg/runtime/type.h b/src/pkg/runtime/type.h index 1adb6dc2e..8c80c62d3 100644 --- a/src/pkg/runtime/type.h +++ b/src/pkg/runtime/type.h @@ -16,8 +16,6 @@ typedef struct UncommonType UncommonType; typedef struct InterfaceType InterfaceType; typedef struct Method Method; typedef struct IMethod IMethod; -typedef struct MapType MapType; -typedef struct ChanType ChanType; typedef struct SliceType SliceType; typedef struct FuncType FuncType; diff --git a/src/pkg/runtime/windows/386/sys.s b/src/pkg/runtime/windows/386/sys.s index d38405075..703f77d55 100644 --- a/src/pkg/runtime/windows/386/sys.s +++ b/src/pkg/runtime/windows/386/sys.s @@ -14,8 +14,6 @@ TEXT runtime·stdcall_raw(SB),7,$0 // Switch to m->g0 if needed. get_tls(DI) MOVL m(DI), DX - MOVL 0(FS), SI - MOVL SI, m_sehframe(DX) MOVL m_g0(DX), SI CMPL g(DI), SI MOVL SP, BX @@ -116,7 +114,7 @@ TEXT runtime·ctrlhandler(SB),7,$0 MOVL SP, BX // setup dummy m, g - SUBL $(m_sehframe+4), SP // at least space for m_sehframe + SUBL $(m_fflag+4), SP // at least space for m_fflag LEAL m_tls(SP), CX MOVL CX, 0x2c(FS) MOVL SP, m(CX) @@ -159,33 +157,39 @@ TEXT runtime·callbackasm+0(SB),7,$0 ADDL $4, DX // extend argsize by size of return value // save registers as required for windows callback - PUSHL 0(FS) PUSHL DI PUSHL SI PUSHL BP PUSHL BX + + // set up SEH frame again + PUSHL $runtime·sigtramp(SB) + PUSHL 0(FS) + MOVL SP, 0(FS) + + // callback parameters PUSHL DX PUSHL CX PUSHL AX - // reinstall our SEH handler - get_tls(CX) - MOVL m(CX), CX - MOVL m_sehframe(CX), CX - MOVL CX, 0(FS) CLD CALL runtime·cgocallback(SB) - // restore registers as required for windows callback POPL AX POPL CX POPL DX + + // pop SEH frame + POPL 0(FS) + POPL BX + + // restore registers as required for windows callback POPL BX POPL BP POPL SI POPL DI - POPL 0(FS) + CLD MOVL -4(CX)(DX*1), AX diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c index 4ab043e88..e08d1b6f0 100644 --- a/src/pkg/runtime/windows/thread.c +++ b/src/pkg/runtime/windows/thread.c @@ -347,16 +347,26 @@ static Callbacks cbs; byte * runtime·compilecallback(Eface fn, bool cleanstack) { - Func *f; - int32 argsize, n; + FuncType *ft; + Type *t; + int32 argsize, i, n; byte *p; Callback *c; - if(fn.type->kind != KindFunc) - runtime·panicstring("not a function"); - if((f = runtime·findfunc((uintptr)fn.data)) == nil) - runtime·throw("cannot find function"); - argsize = (f->args-2) * 4; + if(fn.type == nil || fn.type->kind != KindFunc) + runtime·panicstring("compilecallback: not a function"); + ft = (FuncType*)fn.type; + if(ft->out.len != 1) + runtime·panicstring("compilecallback: function must have one output parameter"); + if(((Type**)ft->out.array)[0]->size != sizeof(uintptr)) + runtime·panicstring("compilecallback: output parameter size is wrong"); + argsize = 0; + for(i=0; iin.len; i++) { + t = ((Type**)ft->in.array)[i]; + if(t->size != sizeof(uintptr)) + runtime·panicstring("compilecallback: input parameter size is wrong"); + argsize += t->size; + } // compute size of new fn. // must match code laid out below. diff --git a/src/pkg/syscall/route_bsd.go b/src/pkg/syscall/route_bsd.go index e41667c14..93e345d71 100644 --- a/src/pkg/syscall/route_bsd.go +++ b/src/pkg/syscall/route_bsd.go @@ -131,35 +131,6 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) { return sas } -const rtaIfmaMask = RTA_GATEWAY | RTA_IFP | RTA_IFA - -func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) { - if m.Header.Addrs&rtaIfmaMask == 0 { - return nil - } - - buf := m.Data[:] - for i := uint(0); i < RTAX_MAX; i++ { - if m.Header.Addrs&rtaIfmaMask&(1<= 0; i-- { + if s.vars[i].name == name { + return s.vars[i].value + } + } + s.errorf("undefined variable: %s", name) + return zero +} + +var zero reflect.Value + +// errorf formats the error and terminates processing. +func (s *state) errorf(format string, args ...interface{}) { + format = fmt.Sprintf("template: %s:%d: %s", s.tmpl.Name(), s.line, format) + panic(fmt.Errorf(format, args...)) +} + +// error terminates processing. +func (s *state) error(err os.Error) { + s.errorf("%s", err) +} + +// errRecover is the handler that turns panics into returns from the top +// level of Parse. +func errRecover(errp *os.Error) { + e := recover() + if e != nil { + if _, ok := e.(runtime.Error); ok { + panic(e) + } + *errp = e.(os.Error) + } +} + +// Execute applies a parsed template to the specified data object, +// writing the output to wr. +func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) { + defer errRecover(&err) + value := reflect.ValueOf(data) + state := &state{ + tmpl: t, + wr: wr, + line: 1, + vars: []variable{{"$", value}}, + } + if t.Root == nil { + state.errorf("must be parsed before execution") + } + state.walk(value, t.Root) + return +} + +// Walk functions step through the major pieces of the template structure, +// generating output as they go. +func (s *state) walk(dot reflect.Value, n parse.Node) { + switch n := n.(type) { + case *parse.ActionNode: + s.line = n.Line + // Do not pop variables so they persist until next end. + // Also, if the action declares variables, don't print the result. + val := s.evalPipeline(dot, n.Pipe) + if len(n.Pipe.Decl) == 0 { + s.printValue(n, val) + } + case *parse.IfNode: + s.line = n.Line + s.walkIfOrWith(parse.NodeIf, dot, n.Pipe, n.List, n.ElseList) + case *parse.ListNode: + for _, node := range n.Nodes { + s.walk(dot, node) + } + case *parse.RangeNode: + s.line = n.Line + s.walkRange(dot, n) + case *parse.TemplateNode: + s.line = n.Line + s.walkTemplate(dot, n) + case *parse.TextNode: + if _, err := s.wr.Write(n.Text); err != nil { + s.error(err) + } + case *parse.WithNode: + s.line = n.Line + s.walkIfOrWith(parse.NodeWith, dot, n.Pipe, n.List, n.ElseList) + default: + s.errorf("unknown node: %s", n) + } +} + +// walkIfOrWith walks an 'if' or 'with' node. The two control structures +// are identical in behavior except that 'with' sets dot. +func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) { + defer s.pop(s.mark()) + val := s.evalPipeline(dot, pipe) + truth, ok := isTrue(val) + if !ok { + s.errorf("if/with can't use %v", val) + } + if truth { + if typ == parse.NodeWith { + s.walk(val, list) + } else { + s.walk(dot, list) + } + } else if elseList != nil { + s.walk(dot, elseList) + } +} + +// isTrue returns whether the value is 'true', in the sense of not the zero of its type, +// and whether the value has a meaningful truth value. +func isTrue(val reflect.Value) (truth, ok bool) { + if !val.IsValid() { + // Something like var x interface{}, never set. It's a form of nil. + return false, true + } + switch val.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + truth = val.Len() > 0 + case reflect.Bool: + truth = val.Bool() + case reflect.Complex64, reflect.Complex128: + truth = val.Complex() != 0 + case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface: + truth = !val.IsNil() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + truth = val.Int() != 0 + case reflect.Float32, reflect.Float64: + truth = val.Float() != 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + truth = val.Uint() != 0 + case reflect.Struct: + truth = true // Struct values are always true. + default: + return + } + return truth, true +} + +func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { + defer s.pop(s.mark()) + val, _ := indirect(s.evalPipeline(dot, r.Pipe)) + // mark top of stack before any variables in the body are pushed. + mark := s.mark() + switch val.Kind() { + case reflect.Array, reflect.Slice: + if val.Len() == 0 { + break + } + for i := 0; i < val.Len(); i++ { + elem := val.Index(i) + // Set top var (lexically the second if there are two) to the element. + if len(r.Pipe.Decl) > 0 { + s.setVar(1, elem) + } + // Set next var (lexically the first if there are two) to the index. + if len(r.Pipe.Decl) > 1 { + s.setVar(2, reflect.ValueOf(i)) + } + s.walk(elem, r.List) + s.pop(mark) + } + return + case reflect.Map: + if val.Len() == 0 { + break + } + for _, key := range val.MapKeys() { + elem := val.MapIndex(key) + // Set top var (lexically the second if there are two) to the element. + if len(r.Pipe.Decl) > 0 { + s.setVar(1, elem) + } + // Set next var (lexically the first if there are two) to the key. + if len(r.Pipe.Decl) > 1 { + s.setVar(2, key) + } + s.walk(elem, r.List) + s.pop(mark) + } + return + case reflect.Invalid: + break // An invalid value is likely a nil map, etc. and acts like an empty map. + default: + s.errorf("range can't iterate over %v", val) + } + if r.ElseList != nil { + s.walk(dot, r.ElseList) + } +} + +func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) { + set := s.tmpl.set + if set == nil { + s.errorf("no set defined in which to invoke template named %q", t.Name) + } + tmpl := set.tmpl[t.Name] + if tmpl == nil { + s.errorf("template %q not in set", t.Name) + } + // Variables declared by the pipeline persist. + dot = s.evalPipeline(dot, t.Pipe) + newState := *s + newState.tmpl = tmpl + // No dynamic scoping: template invocations inherit no variables. + newState.vars = []variable{{"$", dot}} + newState.walk(dot, tmpl.Root) +} + +// Eval functions evaluate pipelines, commands, and their elements and extract +// values from the data structure by examining fields, calling methods, and so on. +// The printing of those values happens only through walk functions. + +// evalPipeline returns the value acquired by evaluating a pipeline. If the +// pipeline has a variable declaration, the variable will be pushed on the +// stack. Callers should therefore pop the stack after they are finished +// executing commands depending on the pipeline value. +func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value reflect.Value) { + if pipe == nil { + return + } + for _, cmd := range pipe.Cmds { + value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg. + // If the object has type interface{}, dig down one level to the thing inside. + if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 { + value = reflect.ValueOf(value.Interface()) // lovely! + } + } + for _, variable := range pipe.Decl { + s.push(variable.Ident[0], value) + } + return value +} + +func (s *state) notAFunction(args []parse.Node, final reflect.Value) { + if len(args) > 1 || final.IsValid() { + s.errorf("can't give argument to non-function %s", args[0]) + } +} + +func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value { + firstWord := cmd.Args[0] + switch n := firstWord.(type) { + case *parse.FieldNode: + return s.evalFieldNode(dot, n, cmd.Args, final) + case *parse.IdentifierNode: + // Must be a function. + return s.evalFunction(dot, n.Ident, cmd.Args, final) + case *parse.VariableNode: + return s.evalVariableNode(dot, n, cmd.Args, final) + } + s.notAFunction(cmd.Args, final) + switch word := firstWord.(type) { + case *parse.BoolNode: + return reflect.ValueOf(word.True) + case *parse.DotNode: + return dot + case *parse.NumberNode: + return s.idealConstant(word) + case *parse.StringNode: + return reflect.ValueOf(word.Text) + } + s.errorf("can't evaluate command %q", firstWord) + panic("not reached") +} + +// idealConstant is called to return the value of a number in a context where +// we don't know the type. In that case, the syntax of the number tells us +// its type, and we use Go rules to resolve. Note there is no such thing as +// a uint ideal constant in this situation - the value must be of int type. +func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value { + // These are ideal constants but we don't know the type + // and we have no context. (If it was a method argument, + // we'd know what we need.) The syntax guides us to some extent. + switch { + case constant.IsComplex: + return reflect.ValueOf(constant.Complex128) // incontrovertible. + case constant.IsFloat && strings.IndexAny(constant.Text, ".eE") >= 0: + return reflect.ValueOf(constant.Float64) + case constant.IsInt: + n := int(constant.Int64) + if int64(n) != constant.Int64 { + s.errorf("%s overflows int", constant.Text) + } + return reflect.ValueOf(n) + case constant.IsUint: + s.errorf("%s overflows int", constant.Text) + } + return zero +} + +func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value { + return s.evalFieldChain(dot, dot, field.Ident, args, final) +} + +func (s *state) evalVariableNode(dot reflect.Value, v *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value { + // $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields. + value := s.varValue(v.Ident[0]) + if len(v.Ident) == 1 { + return value + } + return s.evalFieldChain(dot, value, v.Ident[1:], args, final) +} + +// evalFieldChain evaluates .X.Y.Z possibly followed by arguments. +// dot is the environment in which to evaluate arguments, while +// receiver is the value being walked along the chain. +func (s *state) evalFieldChain(dot, receiver reflect.Value, ident []string, args []parse.Node, final reflect.Value) reflect.Value { + n := len(ident) + for i := 0; i < n-1; i++ { + receiver = s.evalField(dot, ident[i], nil, zero, receiver) + } + // Now if it's a method, it gets the arguments. + return s.evalField(dot, ident[n-1], args, final, receiver) +} + +func (s *state) evalFunction(dot reflect.Value, name string, args []parse.Node, final reflect.Value) reflect.Value { + function, ok := findFunction(name, s.tmpl, s.tmpl.set) + if !ok { + s.errorf("%q is not a defined function", name) + } + return s.evalCall(dot, function, name, args, final) +} + +// evalField evaluates an expression like (.Field) or (.Field arg1 arg2). +// The 'final' argument represents the return value from the preceding +// value of the pipeline, if any. +func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node, final, receiver reflect.Value) reflect.Value { + if !receiver.IsValid() { + return zero + } + typ := receiver.Type() + receiver, _ = indirect(receiver) + // Unless it's an interface, need to get to a value of type *T to guarantee + // we see all methods of T and *T. + ptr := receiver + if ptr.Kind() != reflect.Interface && ptr.CanAddr() { + ptr = ptr.Addr() + } + if method, ok := methodByName(ptr, fieldName); ok { + return s.evalCall(dot, method, fieldName, args, final) + } + hasArgs := len(args) > 1 || final.IsValid() + // It's not a method; is it a field of a struct? + receiver, isNil := indirect(receiver) + if receiver.Kind() == reflect.Struct { + tField, ok := receiver.Type().FieldByName(fieldName) + if ok { + field := receiver.FieldByIndex(tField.Index) + if hasArgs { + s.errorf("%s is not a method but has arguments", fieldName) + } + if tField.PkgPath == "" { // field is exported + return field + } + } + } + // If it's a map, attempt to use the field name as a key. + if receiver.Kind() == reflect.Map { + nameVal := reflect.ValueOf(fieldName) + if nameVal.Type().AssignableTo(receiver.Type().Key()) { + if hasArgs { + s.errorf("%s is not a method but has arguments", fieldName) + } + return receiver.MapIndex(nameVal) + } + } + if isNil { + s.errorf("nil pointer evaluating %s.%s", typ, fieldName) + } + s.errorf("can't evaluate field %s in type %s", fieldName, typ) + panic("not reached") +} + +// TODO: delete when reflect's own MethodByName is released. +func methodByName(receiver reflect.Value, name string) (reflect.Value, bool) { + typ := receiver.Type() + for i := 0; i < typ.NumMethod(); i++ { + if typ.Method(i).Name == name { + return receiver.Method(i), true // This value includes the receiver. + } + } + return zero, false +} + +var ( + osErrorType = reflect.TypeOf((*os.Error)(nil)).Elem() + fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() +) + +// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so +// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0] +// as the function itself. +func (s *state) evalCall(dot, fun reflect.Value, name string, args []parse.Node, final reflect.Value) reflect.Value { + if args != nil { + args = args[1:] // Zeroth arg is function name/node; not passed to function. + } + typ := fun.Type() + numIn := len(args) + if final.IsValid() { + numIn++ + } + numFixed := len(args) + if typ.IsVariadic() { + numFixed = typ.NumIn() - 1 // last arg is the variadic one. + if numIn < numFixed { + s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) + } + } else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() { + s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args)) + } + if !goodFunc(typ) { + s.errorf("can't handle multiple results from method/function %q", name) + } + // Build the arg list. + argv := make([]reflect.Value, numIn) + // Args must be evaluated. Fixed args first. + i := 0 + for ; i < numFixed; i++ { + argv[i] = s.evalArg(dot, typ.In(i), args[i]) + } + // Now the ... args. + if typ.IsVariadic() { + argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice. + for ; i < len(args); i++ { + argv[i] = s.evalArg(dot, argType, args[i]) + } + } + // Add final value if necessary. + if final.IsValid() { + argv[i] = final + } + result := fun.Call(argv) + // If we have an os.Error that is not nil, stop execution and return that error to the caller. + if len(result) == 2 && !result[1].IsNil() { + s.errorf("error calling %s: %s", name, result[1].Interface().(os.Error)) + } + return result[0] +} + +// validateType guarantees that the value is valid and assignable to the type. +func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value { + if !value.IsValid() { + s.errorf("invalid value; expected %s", typ) + } + if !value.Type().AssignableTo(typ) { + s.errorf("wrong type for value; expected %s; got %s", typ, value.Type()) + } + return value +} + +func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value { + switch arg := n.(type) { + case *parse.DotNode: + return s.validateType(dot, typ) + case *parse.FieldNode: + return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, zero), typ) + case *parse.VariableNode: + return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ) + } + switch typ.Kind() { + case reflect.Bool: + return s.evalBool(typ, n) + case reflect.Complex64, reflect.Complex128: + return s.evalComplex(typ, n) + case reflect.Float32, reflect.Float64: + return s.evalFloat(typ, n) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return s.evalInteger(typ, n) + case reflect.Interface: + if typ.NumMethod() == 0 { + return s.evalEmptyInterface(dot, n) + } + case reflect.String: + return s.evalString(typ, n) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return s.evalUnsignedInteger(typ, n) + } + s.errorf("can't handle %s for arg of type %s", n, typ) + panic("not reached") +} + +func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value { + if n, ok := n.(*parse.BoolNode); ok { + value := reflect.New(typ).Elem() + value.SetBool(n.True) + return value + } + s.errorf("expected bool; found %s", n) + panic("not reached") +} + +func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value { + if n, ok := n.(*parse.StringNode); ok { + value := reflect.New(typ).Elem() + value.SetString(n.Text) + return value + } + s.errorf("expected string; found %s", n) + panic("not reached") +} + +func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value { + if n, ok := n.(*parse.NumberNode); ok && n.IsInt { + value := reflect.New(typ).Elem() + value.SetInt(n.Int64) + return value + } + s.errorf("expected integer; found %s", n) + panic("not reached") +} + +func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value { + if n, ok := n.(*parse.NumberNode); ok && n.IsUint { + value := reflect.New(typ).Elem() + value.SetUint(n.Uint64) + return value + } + s.errorf("expected unsigned integer; found %s", n) + panic("not reached") +} + +func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value { + if n, ok := n.(*parse.NumberNode); ok && n.IsFloat { + value := reflect.New(typ).Elem() + value.SetFloat(n.Float64) + return value + } + s.errorf("expected float; found %s", n) + panic("not reached") +} + +func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value { + if n, ok := n.(*parse.NumberNode); ok && n.IsComplex { + value := reflect.New(typ).Elem() + value.SetComplex(n.Complex128) + return value + } + s.errorf("expected complex; found %s", n) + panic("not reached") +} + +func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value { + switch n := n.(type) { + case *parse.BoolNode: + return reflect.ValueOf(n.True) + case *parse.DotNode: + return dot + case *parse.FieldNode: + return s.evalFieldNode(dot, n, nil, zero) + case *parse.IdentifierNode: + return s.evalFunction(dot, n.Ident, nil, zero) + case *parse.NumberNode: + return s.idealConstant(n) + case *parse.StringNode: + return reflect.ValueOf(n.Text) + case *parse.VariableNode: + return s.evalVariableNode(dot, n, nil, zero) + } + s.errorf("can't handle assignment of %s to empty interface argument", n) + panic("not reached") +} + +// indirect returns the item at the end of indirection, and a bool to indicate if it's nil. +// We indirect through pointers and empty interfaces (only) because +// non-empty interfaces have methods we might need. +func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { + for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { + if v.IsNil() { + return v, true + } + if v.Kind() == reflect.Interface && v.NumMethod() > 0 { + break + } + } + return v, false +} + +// printValue writes the textual representation of the value to the output of +// the template. +func (s *state) printValue(n parse.Node, v reflect.Value) { + if v.Kind() == reflect.Ptr { + v, _ = indirect(v) // fmt.Fprint handles nil. + } + if !v.IsValid() { + fmt.Fprint(s.wr, "") + return + } + + if !v.Type().Implements(fmtStringerType) { + if v.CanAddr() && reflect.PtrTo(v.Type()).Implements(fmtStringerType) { + v = v.Addr() + } else { + switch v.Kind() { + case reflect.Chan, reflect.Func: + s.errorf("can't print %s of type %s", n, v.Type()) + } + } + } + fmt.Fprint(s.wr, v.Interface()) +} diff --git a/src/pkg/template/exec_test.go b/src/pkg/template/exec_test.go new file mode 100644 index 000000000..82f56e13c --- /dev/null +++ b/src/pkg/template/exec_test.go @@ -0,0 +1,580 @@ +// 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 template + +import ( + "bytes" + "flag" + "fmt" + "os" + "reflect" + "sort" + "strings" + "testing" +) + +var debug = flag.Bool("debug", false, "show the errors produced by the tests") + +// T has lots of interesting pieces to use to test execution. +type T struct { + // Basics + True bool + I int + U16 uint16 + X string + FloatZero float64 + ComplexZero float64 + // Nested structs. + U *U + // Struct with String method. + V0 V + V1, V2 *V + // Slices + SI []int + SIEmpty []int + SB []bool + // Maps + MSI map[string]int + MSIone map[string]int // one element, for deterministic output + MSIEmpty map[string]int + MXI map[interface{}]int + MII map[int]int + SMSI []map[string]int + // Empty interfaces; used to see if we can dig inside one. + Empty0 interface{} // nil + Empty1 interface{} + Empty2 interface{} + Empty3 interface{} + Empty4 interface{} + // Non-empty interface. + NonEmptyInterface I + // Stringer. + Str fmt.Stringer + // Pointers + PI *int + PSI *[]int + NIL *int + // Template to test evaluation of templates. + Tmpl *Template +} + +type U struct { + V string +} + +type V struct { + j int +} + +func (v *V) String() string { + if v == nil { + return "nilV" + } + return fmt.Sprintf("<%d>", v.j) +} + +var tVal = &T{ + True: true, + I: 17, + U16: 16, + X: "x", + U: &U{"v"}, + V0: V{6666}, + V1: &V{7777}, // leave V2 as nil + SI: []int{3, 4, 5}, + SB: []bool{true, false}, + MSI: map[string]int{"one": 1, "two": 2, "three": 3}, + MSIone: map[string]int{"one": 1}, + MXI: map[interface{}]int{"one": 1}, + MII: map[int]int{1: 1}, + SMSI: []map[string]int{ + {"one": 1, "two": 2}, + {"eleven": 11, "twelve": 12}, + }, + Empty1: 3, + Empty2: "empty2", + Empty3: []int{7, 8}, + Empty4: &U{"UinEmpty"}, + NonEmptyInterface: new(T), + Str: os.NewError("foozle"), + PI: newInt(23), + PSI: newIntSlice(21, 22, 23), + Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X +} + +// A non-empty interface. +type I interface { + Method0() string +} + +var iVal I = tVal + +// Helpers for creation. +func newInt(n int) *int { + p := new(int) + *p = n + return p +} + +func newIntSlice(n ...int) *[]int { + p := new([]int) + *p = make([]int, len(n)) + copy(*p, n) + return p +} + +// Simple methods with and without arguments. +func (t *T) Method0() string { + return "M0" +} + +func (t *T) Method1(a int) int { + return a +} + +func (t *T) Method2(a uint16, b string) string { + return fmt.Sprintf("Method2: %d %s", a, b) +} + +func (t *T) MAdd(a int, b []int) []int { + v := make([]int, len(b)) + for i, x := range b { + v[i] = x + a + } + return v +} + +// MSort is used to sort map keys for stable output. (Nice trick!) +func (t *T) MSort(m map[string]int) []string { + keys := make([]string, len(m)) + i := 0 + for k := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + +// EPERM returns a value and an os.Error according to its argument. +func (t *T) EPERM(error bool) (bool, os.Error) { + if error { + return true, os.EPERM + } + return false, nil +} + +// A few methods to test chaining. +func (t *T) GetU() *U { + return t.U +} + +func (u *U) TrueFalse(b bool) string { + if b { + return "true" + } + return "" +} + +func typeOf(arg interface{}) string { + return fmt.Sprintf("%T", arg) +} + +type execTest struct { + name string + input string + output string + data interface{} + ok bool +} + +// bigInt and bigUint are hex string representing numbers either side +// of the max int boundary. +// We do it this way so the test doesn't depend on ints being 32 bits. +var ( + bigInt = fmt.Sprintf("0x%x", int(1<", tVal, true}, + {"map .one interface", "{{.MXI.one}}", "1", tVal, true}, + {"map .WRONG args", "{{.MSI.one 1}}", "", tVal, false}, + {"map .WRONG type", "{{.MII.one}}", "", tVal, false}, + + // Dots of all kinds to test basic evaluation. + {"dot int", "<{{.}}>", "<13>", 13, true}, + {"dot uint", "<{{.}}>", "<14>", uint(14), true}, + {"dot float", "<{{.}}>", "<15.1>", 15.1, true}, + {"dot bool", "<{{.}}>", "", true, true}, + {"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true}, + {"dot string", "<{{.}}>", "", "hello", true}, + {"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true}, + {"dot map", "<{{.}}>", "", map[string]int{"one": 11, "two": 22}, true}, + {"dot struct", "<{{.}}>", "<{7 seven}>", struct { + a int + b string + }{7, "seven"}, true}, + + // Variables. + {"$ int", "{{$}}", "123", 123, true}, + {"$.I", "{{$.I}}", "17", tVal, true}, + {"$.U.V", "{{$.U.V}}", "v", tVal, true}, + {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true}, + + // Type with String method. + {"V{6666}.String()", "-{{.V0}}-", "-<6666>-", tVal, true}, + {"&V{7777}.String()", "-{{.V1}}-", "-<7777>-", tVal, true}, + {"(*V)(nil).String()", "-{{.V2}}-", "-nilV-", tVal, true}, + + // Pointers. + {"*int", "{{.PI}}", "23", tVal, true}, + {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true}, + {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true}, + {"NIL", "{{.NIL}}", "", tVal, true}, + + // Empty interfaces holding values. + {"empty nil", "{{.Empty0}}", "", tVal, true}, + {"empty with int", "{{.Empty1}}", "3", tVal, true}, + {"empty with string", "{{.Empty2}}", "empty2", tVal, true}, + {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true}, + {"empty with struct", "{{.Empty4}}", "{UinEmpty}", tVal, true}, + {"empty with struct, field", "{{.Empty4.V}}", "UinEmpty", tVal, true}, + + // Method calls. + {".Method0", "-{{.Method0}}-", "-M0-", tVal, true}, + {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true}, + {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true}, + {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true}, + {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true}, + {".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true}, + {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true}, + {"method on chained var", + "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", + "true", tVal, true}, + {"chained method", + "{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", + "true", tVal, true}, + {"chained method on variable", + "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}", + "true", tVal, true}, + + // Pipelines. + {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true}, + + // If. + {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true}, + {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true}, + {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, + {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, + {"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, + {"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true}, + {"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true}, + + // Print etc. + {"print", `{{print "hello, print"}}`, "hello, print", tVal, true}, + {"print", `{{print 1 2 3}}`, "1 2 3", tVal, true}, + {"println", `{{println 1 2 3}}`, "1 2 3\n", tVal, true}, + {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true}, + {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true}, + {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true}, + {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true}, + {"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true}, + {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true}, + {"printf method", `{{printf "%s" .Method0}}`, "M0", tVal, true}, + {"printf dot", `{{with .I}}{{printf "%d" .}}{{end}}`, "17", tVal, true}, + {"printf var", `{{with $x := .I}}{{printf "%d" $x}}{{end}}`, "17", tVal, true}, + {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true}, + + // HTML. + {"html", `{{html ""}}`, + "<script>alert("XSS");</script>", nil, true}, + {"html pipeline", `{{printf "" | html}}`, + "<script>alert("XSS");</script>", nil, true}, + + // JavaScript. + {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true}, + + // URL query. + {"urlquery", `{{"http://www.example.org/"|urlquery}}`, "http%3A%2F%2Fwww.example.org%2F", nil, true}, + + // Booleans + {"not", "{{not true}} {{not false}}", "false true", nil, true}, + {"and", "{{and false 0}} {{and 1 0}} {{and 0 true}} {{and 1 1}}", "false 0 0 1", nil, true}, + {"or", "{{or 0 0}} {{or 1 0}} {{or 0 true}} {{or 1 1}}", "0 1 true 1", nil, true}, + {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true}, + {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true}, + + // Indexing. + {"slice[0]", "{{index .SI 0}}", "3", tVal, true}, + {"slice[1]", "{{index .SI 1}}", "4", tVal, true}, + {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false}, + {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false}, + {"map[one]", "{{index .MSI `one`}}", "1", tVal, true}, + {"map[two]", "{{index .MSI `two`}}", "2", tVal, true}, + {"map[NO]", "{{index .MSI `XXX`}}", "", tVal, true}, + {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false}, + {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true}, + + // Len. + {"slice", "{{len .SI}}", "3", tVal, true}, + {"map", "{{len .MSI }}", "3", tVal, true}, + {"len of int", "{{len 3}}", "", tVal, false}, + {"len of nothing", "{{len .Empty0}}", "", tVal, false}, + + // With. + {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true}, + {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true}, + {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true}, + {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true}, + {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0+1.5i)", tVal, true}, + {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true}, + {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true}, + {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true}, + {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "UinEmpty", tVal, true}, + {"with $x int", "{{with $x := .I}}{{$x}}{{end}}", "17", tVal, true}, + {"with $x struct.U.V", "{{with $x := $}}{{$x.U.V}}{{end}}", "v", tVal, true}, + {"with variable and action", "{{with $x := $}}{{$y := $.U.V}}{{$y}}{{end}}", "v", tVal, true}, + + // Range. + {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true}, + {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, + {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, + {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, + {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, + {"range map", "{{range .MSI | .MSort}}-{{.}}-{{end}}", "-one--three--two-", tVal, true}, + {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true}, + {"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", tVal, true}, + {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true}, + {"range empty nil", "{{range .Empty0}}-{{.}}-{{end}}", "", tVal, true}, + {"range $x SI", "{{range $x := .SI}}<{{$x}}>{{end}}", "<3><4><5>", tVal, true}, + {"range $x $y SI", "{{range $x, $y := .SI}}<{{$x}}={{$y}}>{{end}}", "<0=3><1=4><2=5>", tVal, true}, + {"range $x MSIone", "{{range $x := .MSIone}}<{{$x}}>{{end}}", "<1>", tVal, true}, + {"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "", tVal, true}, + {"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true}, + {"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true}, + + // Cute examples. + {"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true}, + {"or as if false", `{{or .SIEmpty "slice is empty"}}`, "slice is empty", tVal, true}, + + // Error handling. + {"error method, error", "{{.EPERM true}}", "", tVal, false}, + {"error method, no error", "{{.EPERM false}}", "false", tVal, true}, + + // Fixed bugs. + // Must separate dot and receiver; otherwise args are evaluated with dot set to variable. + {"bug0", "{{range .MSIone}}{{if $.Method1 .}}X{{end}}{{end}}", "X", tVal, true}, + // Do not loop endlessly in indirect for non-empty interfaces. + // The bug appears with *interface only; looped forever. + {"bug1", "{{.Method0}}", "M0", &iVal, true}, + // Was taking address of interface field, so method set was empty. + {"bug2", "{{$.NonEmptyInterface.Method0}}", "M0", tVal, true}, + // Struct values were not legal in with - mere oversight. + {"bug3", "{{with $}}{{.Method0}}{{end}}", "M0", tVal, true}, + // Nil interface values in if. + {"bug4", "{{if .Empty0}}non-nil{{else}}nil{{end}}", "nil", tVal, true}, + // Stringer. + {"bug5", "{{.Str}}", "foozle", tVal, true}, +} + +func zeroArgs() string { + return "zeroArgs" +} + +func oneArg(a string) string { + return "oneArg=" + a +} + +func testExecute(execTests []execTest, set *Set, t *testing.T) { + b := new(bytes.Buffer) + funcs := FuncMap{"zeroArgs": zeroArgs, "oneArg": oneArg, "typeOf": typeOf} + for _, test := range execTests { + tmpl := New(test.name).Funcs(funcs) + _, err := tmpl.ParseInSet(test.input, set) + if err != nil { + t.Errorf("%s: parse error: %s", test.name, err) + continue + } + b.Reset() + err = tmpl.Execute(b, test.data) + switch { + case !test.ok && err == nil: + t.Errorf("%s: expected error; got none", test.name) + continue + case test.ok && err != nil: + t.Errorf("%s: unexpected execute error: %s", test.name, err) + continue + case !test.ok && err != nil: + // expected error, got one + if *debug { + fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) + } + } + result := b.String() + if result != test.output { + t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result) + } + } +} + +func TestExecute(t *testing.T) { + testExecute(execTests, nil, t) +} + +// Check that an error from a method flows back to the top. +func TestExecuteError(t *testing.T) { + b := new(bytes.Buffer) + tmpl := New("error") + _, err := tmpl.Parse("{{.EPERM true}}") + if err != nil { + t.Fatalf("parse error: %s", err) + } + err = tmpl.Execute(b, tVal) + if err == nil { + t.Errorf("expected error; got none") + } else if !strings.Contains(err.String(), os.EPERM.String()) { + if *debug { + fmt.Printf("test execute error: %s\n", err) + } + t.Errorf("expected os.EPERM; got %s", err) + } +} + +func TestJSEscaping(t *testing.T) { + testCases := []struct { + in, exp string + }{ + {`a`, `a`}, + {`'foo`, `\'foo`}, + {`Go "jump" \`, `Go \"jump\" \\`}, + {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, + {"unprintable \uFDFF", `unprintable \uFDFF`}, + {``, `\x3Chtml\x3E`}, + } + for _, tc := range testCases { + s := JSEscapeString(tc.in) + if s != tc.exp { + t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp) + } + } +} + +// A nice example: walk a binary tree. + +type Tree struct { + Val int + Left, Right *Tree +} + +const treeTemplate = ` + {{define "tree"}} + [ + {{.Val}} + {{with .Left}} + {{template "tree" .}} + {{end}} + {{with .Right}} + {{template "tree" .}} + {{end}} + ] + {{end}} +` + +func TestTree(t *testing.T) { + var tree = &Tree{ + 1, + &Tree{ + 2, &Tree{ + 3, + &Tree{ + 4, nil, nil, + }, + nil, + }, + &Tree{ + 5, + &Tree{ + 6, nil, nil, + }, + nil, + }, + }, + &Tree{ + 7, + &Tree{ + 8, + &Tree{ + 9, nil, nil, + }, + nil, + }, + &Tree{ + 10, + &Tree{ + 11, nil, nil, + }, + nil, + }, + }, + } + set := new(Set) + _, err := set.Parse(treeTemplate) + if err != nil { + t.Fatal("parse error:", err) + } + var b bytes.Buffer + err = set.Execute(&b, "tree", tree) + if err != nil { + t.Fatal("exec error:", err) + } + stripSpace := func(r int) int { + if r == '\t' || r == '\n' { + return -1 + } + return r + } + result := strings.Map(stripSpace, b.String()) + const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]" + if result != expect { + t.Errorf("expected %q got %q", expect, result) + } +} diff --git a/src/pkg/template/execute.go b/src/pkg/template/execute.go deleted file mode 100644 index 464b620c9..000000000 --- a/src/pkg/template/execute.go +++ /dev/null @@ -1,346 +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. - -// Code to execute a parsed template. - -package template - -import ( - "bytes" - "io" - "reflect" - "strings" -) - -// Internal state for executing a Template. As we evaluate the struct, -// the data item descends into the fields associated with sections, etc. -// Parent is used to walk upwards to find variables higher in the tree. -type state struct { - parent *state // parent in hierarchy - data reflect.Value // the driver data for this section etc. - wr io.Writer // where to send output - buf [2]bytes.Buffer // alternating buffers used when chaining formatters -} - -func (parent *state) clone(data reflect.Value) *state { - return &state{parent: parent, data: data, wr: parent.wr} -} - -// Evaluate interfaces and pointers looking for a value that can look up the name, via a -// struct field, method, or map key, and return the result of the lookup. -func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value { - for v.IsValid() { - typ := v.Type() - if n := v.Type().NumMethod(); n > 0 { - for i := 0; i < n; i++ { - m := typ.Method(i) - mtyp := m.Type - if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { - if !isExported(name) { - t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) - } - return v.Method(i).Call(nil)[0] - } - } - } - switch av := v; av.Kind() { - case reflect.Ptr: - v = av.Elem() - case reflect.Interface: - v = av.Elem() - case reflect.Struct: - if !isExported(name) { - t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) - } - return av.FieldByName(name) - case reflect.Map: - if v := av.MapIndex(reflect.ValueOf(name)); v.IsValid() { - return v - } - return reflect.Zero(typ.Elem()) - default: - return reflect.Value{} - } - } - return v -} - -// indirectPtr returns the item numLevels levels of indirection below the value. -// It is forgiving: if the value is not a pointer, it returns it rather than giving -// an error. If the pointer is nil, it is returned as is. -func indirectPtr(v reflect.Value, numLevels int) reflect.Value { - for i := numLevels; v.IsValid() && i > 0; i++ { - if p := v; p.Kind() == reflect.Ptr { - if p.IsNil() { - return v - } - v = p.Elem() - } else { - break - } - } - return v -} - -// Walk v through pointers and interfaces, extracting the elements within. -func indirect(v reflect.Value) reflect.Value { -loop: - for v.IsValid() { - switch av := v; av.Kind() { - case reflect.Ptr: - v = av.Elem() - case reflect.Interface: - v = av.Elem() - default: - break loop - } - } - return v -} - -// If the data for this template is a struct, find the named variable. -// Names of the form a.b.c are walked down the data tree. -// The special name "@" (the "cursor") denotes the current data. -// The value coming in (st.data) might need indirecting to reach -// a struct while the return value is not indirected - that is, -// it represents the actual named field. Leading stars indicate -// levels of indirection to be applied to the value. -func (t *Template) findVar(st *state, s string) reflect.Value { - data := st.data - flattenedName := strings.TrimLeft(s, "*") - numStars := len(s) - len(flattenedName) - s = flattenedName - if s == "@" { - return indirectPtr(data, numStars) - } - for _, elem := range strings.Split(s, ".") { - // Look up field; data must be a struct or map. - data = t.lookup(st, data, elem) - if !data.IsValid() { - return reflect.Value{} - } - } - return indirectPtr(data, numStars) -} - -// Is there no data to look at? -func empty(v reflect.Value) bool { - v = indirect(v) - if !v.IsValid() { - return true - } - switch v.Kind() { - case reflect.Bool: - return v.Bool() == false - case reflect.String: - return v.String() == "" - case reflect.Struct: - return false - case reflect.Map: - return false - case reflect.Array: - return v.Len() == 0 - case reflect.Slice: - return v.Len() == 0 - } - return false -} - -// Look up a variable or method, up through the parent if necessary. -func (t *Template) varValue(name string, st *state) reflect.Value { - field := t.findVar(st, name) - if !field.IsValid() { - if st.parent == nil { - t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type()) - } - return t.varValue(name, st.parent) - } - return field -} - -func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) { - fn := t.formatter(fmt) - if fn == nil { - t.execError(st, v.linenum, "missing formatter %s for variable", fmt) - } - fn(wr, fmt, val...) -} - -// Evaluate a variable, looking up through the parent if necessary. -// If it has a formatter attached ({var|formatter}) run that too. -func (t *Template) writeVariable(v *variableElement, st *state) { - // Resolve field names - val := make([]interface{}, len(v.args)) - for i, arg := range v.args { - if name, ok := arg.(fieldName); ok { - val[i] = t.varValue(string(name), st).Interface() - } else { - val[i] = arg - } - } - for i, fmt := range v.fmts[:len(v.fmts)-1] { - b := &st.buf[i&1] - b.Reset() - t.format(b, fmt, val, v, st) - val = val[0:1] - val[0] = b.Bytes() - } - t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st) -} - -// Execute element i. Return next index to execute. -func (t *Template) executeElement(i int, st *state) int { - switch elem := t.elems[i].(type) { - case *textElement: - st.wr.Write(elem.text) - return i + 1 - case *literalElement: - st.wr.Write(elem.text) - return i + 1 - case *variableElement: - t.writeVariable(elem, st) - return i + 1 - case *sectionElement: - t.executeSection(elem, st) - return elem.end - case *repeatedElement: - t.executeRepeated(elem, st) - return elem.end - } - e := t.elems[i] - t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.ValueOf(e).Interface(), e) - return 0 -} - -// Execute the template. -func (t *Template) execute(start, end int, st *state) { - for i := start; i < end; { - i = t.executeElement(i, st) - } -} - -// Execute a .section -func (t *Template) executeSection(s *sectionElement, st *state) { - // Find driver data for this section. It must be in the current struct. - field := t.varValue(s.field, st) - if !field.IsValid() { - t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type()) - } - st = st.clone(field) - start, end := s.start, s.or - if !empty(field) { - // Execute the normal block. - if end < 0 { - end = s.end - } - } else { - // Execute the .or block. If it's missing, do nothing. - start, end = s.or, s.end - if start < 0 { - return - } - } - for i := start; i < end; { - i = t.executeElement(i, st) - } -} - -// Return the result of calling the Iter method on v, or nil. -func iter(v reflect.Value) reflect.Value { - for j := 0; j < v.Type().NumMethod(); j++ { - mth := v.Type().Method(j) - fv := v.Method(j) - ft := fv.Type() - // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue. - if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 { - continue - } - ct := ft.Out(0) - if ct.Kind() != reflect.Chan || - ct.ChanDir()&reflect.RecvDir == 0 { - continue - } - return fv.Call(nil)[0] - } - return reflect.Value{} -} - -// Execute a .repeated section -func (t *Template) executeRepeated(r *repeatedElement, st *state) { - // Find driver data for this section. It must be in the current struct. - field := t.varValue(r.field, st) - if !field.IsValid() { - t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type()) - } - field = indirect(field) - - start, end := r.start, r.or - if end < 0 { - end = r.end - } - if r.altstart >= 0 { - end = r.altstart - } - first := true - - // Code common to all the loops. - loopBody := func(newst *state) { - // .alternates between elements - if !first && r.altstart >= 0 { - for i := r.altstart; i < r.altend; { - i = t.executeElement(i, newst) - } - } - first = false - for i := start; i < end; { - i = t.executeElement(i, newst) - } - } - - if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice { - for j := 0; j < array.Len(); j++ { - loopBody(st.clone(array.Index(j))) - } - } else if m := field; m.Kind() == reflect.Map { - for _, key := range m.MapKeys() { - loopBody(st.clone(m.MapIndex(key))) - } - } else if ch := iter(field); ch.IsValid() { - for { - e, ok := ch.Recv() - if !ok { - break - } - loopBody(st.clone(e)) - } - } else { - t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)", - r.field, field.Type()) - } - - if first { - // Empty. Execute the .or block, once. If it's missing, do nothing. - start, end := r.or, r.end - if start >= 0 { - newst := st.clone(field) - for i := start; i < end; { - i = t.executeElement(i, newst) - } - } - return - } -} - -// A valid delimiter must contain no space and be non-empty. -func validDelim(d []byte) bool { - if len(d) == 0 { - return false - } - for _, c := range d { - if isSpace(c) { - return false - } - } - return true -} diff --git a/src/pkg/template/format.go b/src/pkg/template/format.go deleted file mode 100644 index 9156b0808..000000000 --- a/src/pkg/template/format.go +++ /dev/null @@ -1,77 +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. - -// Template library: default formatters - -package template - -import ( - "bytes" - "fmt" - "io" -) - -// StringFormatter formats into the default string representation. -// It is stored under the name "str" and is the default formatter. -// You can override the default formatter by storing your default -// under the name "" in your custom formatter map. -func StringFormatter(w io.Writer, format string, value ...interface{}) { - if len(value) == 1 { - if b, ok := value[0].([]byte); ok { - w.Write(b) - return - } - } - fmt.Fprint(w, value...) -} - -var ( - esc_quot = []byte(""") // shorter than """ - esc_apos = []byte("'") // shorter than "'" - esc_amp = []byte("&") - esc_lt = []byte("<") - esc_gt = []byte(">") -) - -// HTMLEscape writes to w the properly escaped HTML equivalent -// of the plain text data s. -func HTMLEscape(w io.Writer, s []byte) { - var esc []byte - last := 0 - for i, c := range s { - switch c { - case '"': - esc = esc_quot - case '\'': - esc = esc_apos - case '&': - esc = esc_amp - case '<': - esc = esc_lt - case '>': - esc = esc_gt - default: - continue - } - w.Write(s[last:i]) - w.Write(esc) - last = i + 1 - } - w.Write(s[last:]) -} - -// HTMLFormatter formats arbitrary values for HTML -func HTMLFormatter(w io.Writer, format string, value ...interface{}) { - ok := false - var b []byte - if len(value) == 1 { - b, ok = value[0].([]byte) - } - if !ok { - var buf bytes.Buffer - fmt.Fprint(&buf, value...) - b = buf.Bytes() - } - HTMLEscape(w, b) -} diff --git a/src/pkg/template/funcs.go b/src/pkg/template/funcs.go new file mode 100644 index 000000000..feb1fd82c --- /dev/null +++ b/src/pkg/template/funcs.go @@ -0,0 +1,368 @@ +// 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 template + +import ( + "bytes" + "fmt" + "io" + "os" + "reflect" + "strings" + "unicode" + "url" + "utf8" +) + +// FuncMap is the type of the map defining the mapping from names to functions. +// Each function must have either a single return value, or two return values of +// which the second has type os.Error. If the second argument evaluates to non-nil +// during execution, execution terminates and Execute returns an error. +type FuncMap map[string]interface{} + +var builtins = FuncMap{ + "and": and, + "html": HTMLEscaper, + "index": index, + "js": JSEscaper, + "len": length, + "not": not, + "or": or, + "print": fmt.Sprint, + "printf": fmt.Sprintf, + "println": fmt.Sprintln, + "urlquery": URLQueryEscaper, +} + +var builtinFuncs = createValueFuncs(builtins) + +// createValueFuncs turns a FuncMap into a map[string]reflect.Value +func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { + m := make(map[string]reflect.Value) + addValueFuncs(m, funcMap) + return m +} + +// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values. +func addValueFuncs(out map[string]reflect.Value, in FuncMap) { + for name, fn := range in { + v := reflect.ValueOf(fn) + if v.Kind() != reflect.Func { + panic("value for " + name + " not a function") + } + if !goodFunc(v.Type()) { + panic(fmt.Errorf("can't handle multiple results from method/function %q", name)) + } + out[name] = v + } +} + +// addFuncs adds to values the functions in funcs. It does no checking of the input - +// call addValueFuncs first. +func addFuncs(out, in FuncMap) { + for name, fn := range in { + out[name] = fn + } +} + +// goodFunc checks that the function or method has the right result signature. +func goodFunc(typ reflect.Type) bool { + // We allow functions with 1 result or 2 results where the second is an os.Error. + switch { + case typ.NumOut() == 1: + return true + case typ.NumOut() == 2 && typ.Out(1) == osErrorType: + return true + } + return false +} + +// findFunction looks for a function in the template, set, and global map. +func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) { + if tmpl != nil { + if fn := tmpl.execFuncs[name]; fn.IsValid() { + return fn, true + } + } + if set != nil { + if fn := set.execFuncs[name]; fn.IsValid() { + return fn, true + } + } + if fn := builtinFuncs[name]; fn.IsValid() { + return fn, true + } + return reflect.Value{}, false +} + +// Indexing. + +// index returns the result of indexing its first argument by the following +// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each +// indexed item must be a map, slice, or array. +func index(item interface{}, indices ...interface{}) (interface{}, os.Error) { + v := reflect.ValueOf(item) + for _, i := range indices { + index := reflect.ValueOf(i) + var isNil bool + if v, isNil = indirect(v); isNil { + return nil, fmt.Errorf("index of nil pointer") + } + switch v.Kind() { + case reflect.Array, reflect.Slice: + var x int64 + switch index.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x = index.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + x = int64(index.Uint()) + default: + return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type()) + } + if x < 0 || x >= int64(v.Len()) { + return nil, fmt.Errorf("index out of range: %d", x) + } + v = v.Index(int(x)) + case reflect.Map: + if !index.Type().AssignableTo(v.Type().Key()) { + return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type()) + } + if x := v.MapIndex(index); x.IsValid() { + v = x + } else { + v = reflect.Zero(v.Type().Key()) + } + default: + return nil, fmt.Errorf("can't index item of type %s", index.Type()) + } + } + return v.Interface(), nil +} + +// Length + +// length returns the length of the item, with an error if it has no defined length. +func length(item interface{}) (int, os.Error) { + v, isNil := indirect(reflect.ValueOf(item)) + if isNil { + return 0, fmt.Errorf("len of nil pointer") + } + switch v.Kind() { + case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: + return v.Len(), nil + } + return 0, fmt.Errorf("len of type %s", v.Type()) +} + +// Boolean logic. + +func truth(a interface{}) bool { + t, _ := isTrue(reflect.ValueOf(a)) + return t +} + +// and computes the Boolean AND of its arguments, returning +// the first false argument it encounters, or the last argument. +func and(arg0 interface{}, args ...interface{}) interface{} { + if !truth(arg0) { + return arg0 + } + for i := range args { + arg0 = args[i] + if !truth(arg0) { + break + } + } + return arg0 +} + +// or computes the Boolean OR of its arguments, returning +// the first true argument it encounters, or the last argument. +func or(arg0 interface{}, args ...interface{}) interface{} { + if truth(arg0) { + return arg0 + } + for i := range args { + arg0 = args[i] + if truth(arg0) { + break + } + } + return arg0 +} + +// not returns the Boolean negation of its argument. +func not(arg interface{}) (truth bool) { + truth, _ = isTrue(reflect.ValueOf(arg)) + return !truth +} + +// HTML escaping. + +var ( + htmlQuot = []byte(""") // shorter than """ + htmlApos = []byte("'") // shorter than "'" + htmlAmp = []byte("&") + htmlLt = []byte("<") + htmlGt = []byte(">") +) + +// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. +func HTMLEscape(w io.Writer, b []byte) { + last := 0 + for i, c := range b { + var html []byte + switch c { + case '"': + html = htmlQuot + case '\'': + html = htmlApos + case '&': + html = htmlAmp + case '<': + html = htmlLt + case '>': + html = htmlGt + default: + continue + } + w.Write(b[last:i]) + w.Write(html) + last = i + 1 + } + w.Write(b[last:]) +} + +// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. +func HTMLEscapeString(s string) string { + // Avoid allocation if we can. + if strings.IndexAny(s, `'"&<>`) < 0 { + return s + } + var b bytes.Buffer + HTMLEscape(&b, []byte(s)) + return b.String() +} + +// HTMLEscaper returns the escaped HTML equivalent of the textual +// representation of its arguments. +func HTMLEscaper(args ...interface{}) string { + ok := false + var s string + if len(args) == 1 { + s, ok = args[0].(string) + } + if !ok { + s = fmt.Sprint(args...) + } + return HTMLEscapeString(s) +} + +// JavaScript escaping. + +var ( + jsLowUni = []byte(`\u00`) + hex = []byte("0123456789ABCDEF") + + jsBackslash = []byte(`\\`) + jsApos = []byte(`\'`) + jsQuot = []byte(`\"`) + jsLt = []byte(`\x3C`) + jsGt = []byte(`\x3E`) +) + +// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. +func JSEscape(w io.Writer, b []byte) { + last := 0 + for i := 0; i < len(b); i++ { + c := b[i] + + if !jsIsSpecial(int(c)) { + // fast path: nothing to do + continue + } + w.Write(b[last:i]) + + if c < utf8.RuneSelf { + // Quotes, slashes and angle brackets get quoted. + // Control characters get written as \u00XX. + switch c { + case '\\': + w.Write(jsBackslash) + case '\'': + w.Write(jsApos) + case '"': + w.Write(jsQuot) + case '<': + w.Write(jsLt) + case '>': + w.Write(jsGt) + default: + w.Write(jsLowUni) + t, b := c>>4, c&0x0f + w.Write(hex[t : t+1]) + w.Write(hex[b : b+1]) + } + } else { + // Unicode rune. + rune, size := utf8.DecodeRune(b[i:]) + if unicode.IsPrint(rune) { + w.Write(b[i : i+size]) + } else { + // TODO(dsymonds): Do this without fmt? + fmt.Fprintf(w, "\\u%04X", rune) + } + i += size - 1 + } + last = i + 1 + } + w.Write(b[last:]) +} + +// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s. +func JSEscapeString(s string) string { + // Avoid allocation if we can. + if strings.IndexFunc(s, jsIsSpecial) < 0 { + return s + } + var b bytes.Buffer + JSEscape(&b, []byte(s)) + return b.String() +} + +func jsIsSpecial(rune int) bool { + switch rune { + case '\\', '\'', '"', '<', '>': + return true + } + return rune < ' ' || utf8.RuneSelf <= rune +} + +// JSEscaper returns the escaped JavaScript equivalent of the textual +// representation of its arguments. +func JSEscaper(args ...interface{}) string { + ok := false + var s string + if len(args) == 1 { + s, ok = args[0].(string) + } + if !ok { + s = fmt.Sprint(args...) + } + return JSEscapeString(s) +} + +// URLQueryEscaper returns the escaped value of the textual representation of +// its arguments in a form suitable for embedding in a URL query. +func URLQueryEscaper(args ...interface{}) string { + s, ok := "", false + if len(args) == 1 { + s, ok = args[0].(string) + } + if !ok { + s = fmt.Sprint(args...) + } + return url.QueryEscape(s) +} diff --git a/src/pkg/template/helper.go b/src/pkg/template/helper.go new file mode 100644 index 000000000..c9b099856 --- /dev/null +++ b/src/pkg/template/helper.go @@ -0,0 +1,238 @@ +// 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. + +// Helper functions to make constructing templates and sets easier. + +package template + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +// Functions and methods to parse a single template. + +// Must is a helper that wraps a call to a function returning (*Template, os.Error) +// and panics if the error is non-nil. It is intended for use in variable initializations +// such as +// var t = template.Must(template.New("name").Parse("text")) +func Must(t *Template, err os.Error) *Template { + if err != nil { + panic(err) + } + return t +} + +// ParseFile creates a new Template and parses the template definition from +// the named file. The template name is the base name of the file. +func ParseFile(filename string) (*Template, os.Error) { + t := New(filepath.Base(filename)) + return t.ParseFile(filename) +} + +// parseFileInSet creates a new Template and parses the template +// definition from the named file. The template name is the base name +// of the file. It also adds the template to the set. Function bindings are +// checked against those in the set. +func parseFileInSet(filename string, set *Set) (*Template, os.Error) { + t := New(filepath.Base(filename)) + return t.parseFileInSet(filename, set) +} + +// ParseFile reads the template definition from a file and parses it to +// construct an internal representation of the template for execution. +// The returned template will be nil if an error occurs. +func (t *Template) ParseFile(filename string) (*Template, os.Error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return t.Parse(string(b)) +} + +// parseFileInSet is the same as ParseFile except that function bindings +// are checked against those in the set and the template is added +// to the set. +// The returned template will be nil if an error occurs. +func (t *Template) parseFileInSet(filename string, set *Set) (*Template, os.Error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return t.ParseInSet(string(b), set) +} + +// Functions and methods to parse a set. + +// SetMust is a helper that wraps a call to a function returning (*Set, os.Error) +// and panics if the error is non-nil. It is intended for use in variable initializations +// such as +// var s = template.SetMust(template.ParseSetFiles("file")) +func SetMust(s *Set, err os.Error) *Set { + if err != nil { + panic(err) + } + return s +} + +// ParseFiles parses the named files into a set of named templates. +// Each file must be parseable by itself. +// If an error occurs, parsing stops and the returned set is nil. +func (s *Set) ParseFiles(filenames ...string) (*Set, os.Error) { + for _, filename := range filenames { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + _, err = s.Parse(string(b)) + if err != nil { + return nil, err + } + } + return s, nil +} + +// ParseSetFiles creates a new Set and parses the set definition from the +// named files. Each file must be individually parseable. +func ParseSetFiles(filenames ...string) (*Set, os.Error) { + s := new(Set) + for _, filename := range filenames { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + _, err = s.Parse(string(b)) + if err != nil { + return nil, err + } + } + return s, nil +} + +// ParseGlob parses the set definition from the files identified by the +// pattern. The pattern is processed by filepath.Glob and must match at +// least one file. +// If an error occurs, parsing stops and the returned set is nil. +func (s *Set) ParseGlob(pattern string) (*Set, os.Error) { + filenames, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + if len(filenames) == 0 { + return nil, fmt.Errorf("pattern matches no files: %#q", pattern) + } + return s.ParseFiles(filenames...) +} + +// ParseSetGlob creates a new Set and parses the set definition from the +// files identified by the pattern. The pattern is processed by filepath.Glob +// and must match at least one file. +func ParseSetGlob(pattern string) (*Set, os.Error) { + set, err := new(Set).ParseGlob(pattern) + if err != nil { + return nil, err + } + return set, nil +} + +// Functions and methods to parse stand-alone template files into a set. + +// ParseTemplateFiles parses the named template files and adds +// them to the set. Each template will be named the base name of +// its file. +// Unlike with ParseFiles, each file should be a stand-alone template +// definition suitable for Template.Parse (not Set.Parse); that is, the +// file does not contain {{define}} clauses. ParseTemplateFiles is +// therefore equivalent to calling the ParseFile function to create +// individual templates, which are then added to the set. +// Each file must be parseable by itself. +// If an error occurs, parsing stops and the returned set is nil. +func (s *Set) ParseTemplateFiles(filenames ...string) (*Set, os.Error) { + for _, filename := range filenames { + _, err := parseFileInSet(filename, s) + if err != nil { + return nil, err + } + } + return s, nil +} + +// ParseTemplateGlob parses the template files matched by the +// patern and adds them to the set. Each template will be named +// the base name of its file. +// Unlike with ParseGlob, each file should be a stand-alone template +// definition suitable for Template.Parse (not Set.Parse); that is, the +// file does not contain {{define}} clauses. ParseTemplateGlob is +// therefore equivalent to calling the ParseFile function to create +// individual templates, which are then added to the set. +// Each file must be parseable by itself. +// If an error occurs, parsing stops and the returned set is nil. +func (s *Set) ParseTemplateGlob(pattern string) (*Set, os.Error) { + filenames, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + for _, filename := range filenames { + _, err := parseFileInSet(filename, s) + if err != nil { + return nil, err + } + } + return s, nil +} + +// ParseTemplateFiles creates a set by parsing the named files, +// each of which defines a single template. Each template will be +// named the base name of its file. +// Unlike with ParseFiles, each file should be a stand-alone template +// definition suitable for Template.Parse (not Set.Parse); that is, the +// file does not contain {{define}} clauses. ParseTemplateFiles is +// therefore equivalent to calling the ParseFile function to create +// individual templates, which are then added to the set. +// Each file must be parseable by itself. Parsing stops if an error is +// encountered. +func ParseTemplateFiles(filenames ...string) (*Set, os.Error) { + set := new(Set) + set.init() + for _, filename := range filenames { + t, err := ParseFile(filename) + if err != nil { + return nil, err + } + if err := set.add(t); err != nil { + return nil, err + } + } + return set, nil +} + +// ParseTemplateGlob creates a set by parsing the files matched +// by the pattern, each of which defines a single template. Each +// template will be named the base name of its file. +// Unlike with ParseGlob, each file should be a stand-alone template +// definition suitable for Template.Parse (not Set.Parse); that is, the +// file does not contain {{define}} clauses. ParseTemplateGlob is +// therefore equivalent to calling the ParseFile function to create +// individual templates, which are then added to the set. +// Each file must be parseable by itself. Parsing stops if an error is +// encountered. +func ParseTemplateGlob(pattern string) (*Set, os.Error) { + set := new(Set) + filenames, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + for _, filename := range filenames { + t, err := ParseFile(filename) + if err != nil { + return nil, err + } + if err := set.add(t); err != nil { + return nil, err + } + } + return set, nil +} diff --git a/src/pkg/template/parse.go b/src/pkg/template/parse.go index dedf9ad8e..b089c599a 100644 --- a/src/pkg/template/parse.go +++ b/src/pkg/template/parse.go @@ -1,743 +1,85 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// 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. -// Code to parse a template. - package template import ( - "fmt" - "io" - "io/ioutil" "os" "reflect" - "strconv" - "strings" - "unicode" - "utf8" -) - -// Errors returned during parsing and execution. Users may extract the information and reformat -// if they desire. -type Error struct { - Line int - Msg string -} - -func (e *Error) String() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) } - -// checkError is a deferred function to turn a panic with type *Error into a plain error return. -// Other panics are unexpected and so are re-enabled. -func checkError(error *os.Error) { - if v := recover(); v != nil { - if e, ok := v.(*Error); ok { - *error = e - } else { - // runtime errors should crash - panic(v) - } - } -} - -// Most of the literals are aces. -var lbrace = []byte{'{'} -var rbrace = []byte{'}'} -var space = []byte{' '} -var tab = []byte{'\t'} - -// The various types of "tokens", which are plain text or (usually) brace-delimited descriptors -const ( - tokAlternates = iota - tokComment - tokEnd - tokLiteral - tokOr - tokRepeated - tokSection - tokText - tokVariable + "template/parse" ) -// FormatterMap is the type describing the mapping from formatter -// names to the functions that implement them. -type FormatterMap map[string]func(io.Writer, string, ...interface{}) - -// Built-in formatters. -var builtins = FormatterMap{ - "html": HTMLFormatter, - "str": StringFormatter, - "": StringFormatter, -} - -// The parsed state of a template is a vector of xxxElement structs. -// Sections have line numbers so errors can be reported better during execution. - -// Plain text. -type textElement struct { - text []byte -} - -// A literal such as .meta-left or .meta-right -type literalElement struct { - text []byte -} - -// A variable invocation to be evaluated -type variableElement struct { - linenum int - args []interface{} // The fields and literals in the invocation. - fmts []string // Names of formatters to apply. len(fmts) > 0 -} - -// A variableElement arg to be evaluated as a field name -type fieldName string - -// A .section block, possibly with a .or -type sectionElement struct { - linenum int // of .section itself - field string // cursor field for this block - start int // first element - or int // first element of .or block - end int // one beyond last element -} - -// A .repeated block, possibly with a .or and a .alternates -type repeatedElement struct { - sectionElement // It has the same structure... - altstart int // ... except for alternates - altend int -} - -// Template is the type that represents a template definition. -// It is unchanged after parsing. +// Template is the representation of a parsed template. type Template struct { - fmap FormatterMap // formatters for variables - // Used during parsing: - ldelim, rdelim []byte // delimiters; default {} - buf []byte // input text to process - p int // position in buf - linenum int // position in input - // Parsed results: - elems []interface{} -} - -// New creates a new template with the specified formatter map (which -// may be nil) to define auxiliary functions for formatting variables. -func New(fmap FormatterMap) *Template { - t := new(Template) - t.fmap = fmap - t.ldelim = lbrace - t.rdelim = rbrace - t.elems = make([]interface{}, 0, 16) - return t -} - -// Report error and stop executing. The line number must be provided explicitly. -func (t *Template) execError(st *state, line int, err string, args ...interface{}) { - panic(&Error{line, fmt.Sprintf(err, args...)}) -} - -// Report error, panic to terminate parsing. -// The line number comes from the template state. -func (t *Template) parseError(err string, args ...interface{}) { - panic(&Error{t.linenum, fmt.Sprintf(err, args...)}) + name string + *parse.Tree + // We use two maps, one for parsing and one for execution. + // This separation makes the API cleaner since it doesn't + // expose reflection to the client. + parseFuncs FuncMap + execFuncs map[string]reflect.Value + set *Set // can be nil. } -// Is this an exported - upper case - name? -func isExported(name string) bool { - rune, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(rune) +// Name returns the name of the template. +func (t *Template) Name() string { + return t.name } -// -- Lexical analysis +// Parsing. -// Is c a space character? -func isSpace(c uint8) bool { return c == ' ' || c == '\t' || c == '\r' || c == '\n' } - -// Safely, does s[n:n+len(t)] == t? -func equal(s []byte, n int, t []byte) bool { - b := s[n:] - if len(t) > len(b) { // not enough space left for a match. - return false - } - for i, c := range t { - if c != b[i] { - return false - } - } - return true -} - -// isQuote returns true if c is a string- or character-delimiting quote character. -func isQuote(c byte) bool { - return c == '"' || c == '`' || c == '\'' -} - -// endQuote returns the end quote index for the quoted string that -// starts at n, or -1 if no matching end quote is found before the end -// of the line. -func endQuote(s []byte, n int) int { - quote := s[n] - for n++; n < len(s); n++ { - switch s[n] { - case '\\': - if quote == '"' || quote == '\'' { - n++ - } - case '\n': - return -1 - case quote: - return n - } - } - return -1 -} - -// nextItem returns the next item from the input buffer. If the returned -// item is empty, we are at EOF. The item will be either a -// delimited string or a non-empty string between delimited -// strings. Tokens stop at (but include, if plain text) a newline. -// Action tokens on a line by themselves drop any space on -// either side, up to and including the newline. -func (t *Template) nextItem() []byte { - startOfLine := t.p == 0 || t.buf[t.p-1] == '\n' - start := t.p - var i int - newline := func() { - t.linenum++ - i++ - } - // Leading space up to but not including newline - for i = start; i < len(t.buf); i++ { - if t.buf[i] == '\n' || !isSpace(t.buf[i]) { - break - } - } - leadingSpace := i > start - // What's left is nothing, newline, delimited string, or plain text - switch { - case i == len(t.buf): - // EOF; nothing to do - case t.buf[i] == '\n': - newline() - case equal(t.buf, i, t.ldelim): - left := i // Start of left delimiter. - right := -1 // Will be (immediately after) right delimiter. - haveText := false // Delimiters contain text. - i += len(t.ldelim) - // Find the end of the action. - for ; i < len(t.buf); i++ { - if t.buf[i] == '\n' { - break - } - if isQuote(t.buf[i]) { - i = endQuote(t.buf, i) - if i == -1 { - t.parseError("unmatched quote") - return nil - } - continue - } - if equal(t.buf, i, t.rdelim) { - i += len(t.rdelim) - right = i - break - } - haveText = true - } - if right < 0 { - t.parseError("unmatched opening delimiter") - return nil - } - // Is this a special action (starts with '.' or '#') and the only thing on the line? - if startOfLine && haveText { - firstChar := t.buf[left+len(t.ldelim)] - if firstChar == '.' || firstChar == '#' { - // It's special and the first thing on the line. Is it the last? - for j := right; j < len(t.buf) && isSpace(t.buf[j]); j++ { - if t.buf[j] == '\n' { - // Yes it is. Drop the surrounding space and return the {.foo} - t.linenum++ - t.p = j + 1 - return t.buf[left:right] - } - } - } - } - // No it's not. If there's leading space, return that. - if leadingSpace { - // not trimming space: return leading space if there is some. - t.p = left - return t.buf[start:left] - } - // Return the word, leave the trailing space. - start = left - break - default: - for ; i < len(t.buf); i++ { - if t.buf[i] == '\n' { - newline() - break - } - if equal(t.buf, i, t.ldelim) { - break - } - } +// New allocates a new template with the given name. +func New(name string) *Template { + return &Template{ + name: name, + parseFuncs: make(FuncMap), + execFuncs: make(map[string]reflect.Value), } - item := t.buf[start:i] - t.p = i - return item } -// Turn a byte array into a space-split array of strings, -// taking into account quoted strings. -func words(buf []byte) []string { - s := make([]string, 0, 5) - for i := 0; i < len(buf); { - // One word per loop - for i < len(buf) && isSpace(buf[i]) { - i++ - } - if i == len(buf) { - break - } - // Got a word - start := i - if isQuote(buf[i]) { - i = endQuote(buf, i) - if i < 0 { - i = len(buf) - } else { - i++ - } - } - // Even with quotes, break on space only. This handles input - // such as {""|} and catches quoting mistakes. - for i < len(buf) && !isSpace(buf[i]) { - i++ - } - s = append(s, string(buf[start:i])) - } - return s -} - -// Analyze an item and return its token type and, if it's an action item, an array of -// its constituent words. -func (t *Template) analyze(item []byte) (tok int, w []string) { - // item is known to be non-empty - if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter - tok = tokText - return - } - if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter - t.parseError("internal error: unmatched opening delimiter") // lexing should prevent this - return - } - if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents - t.parseError("empty directive") - return - } - // Comment - if item[len(t.ldelim)] == '#' { - tok = tokComment - return - } - // Split into words - w = words(item[len(t.ldelim) : len(item)-len(t.rdelim)]) // drop final delimiter - if len(w) == 0 { - t.parseError("empty directive") - return - } - first := w[0] - if first[0] != '.' { - tok = tokVariable - return - } - if len(first) > 1 && first[1] >= '0' && first[1] <= '9' { - // Must be a float. - tok = tokVariable - return - } - switch first { - case ".meta-left", ".meta-right", ".space", ".tab": - tok = tokLiteral - return - case ".or": - tok = tokOr - return - case ".end": - tok = tokEnd - return - case ".section": - if len(w) != 2 { - t.parseError("incorrect fields for .section: %s", item) - return - } - tok = tokSection - return - case ".repeated": - if len(w) != 3 || w[1] != "section" { - t.parseError("incorrect fields for .repeated: %s", item) - return - } - tok = tokRepeated - return - case ".alternates": - if len(w) != 2 || w[1] != "with" { - t.parseError("incorrect fields for .alternates: %s", item) - return - } - tok = tokAlternates - return - } - t.parseError("bad directive: %s", item) - return -} - -// formatter returns the Formatter with the given name in the Template, or nil if none exists. -func (t *Template) formatter(name string) func(io.Writer, string, ...interface{}) { - if t.fmap != nil { - if fn := t.fmap[name]; fn != nil { - return fn - } - } - return builtins[name] -} - -// -- Parsing - -// newVariable allocates a new variable-evaluation element. -func (t *Template) newVariable(words []string) *variableElement { - formatters := extractFormatters(words) - args := make([]interface{}, len(words)) - - // Build argument list, processing any literals - for i, word := range words { - var lerr os.Error - switch word[0] { - case '"', '`', '\'': - v, err := strconv.Unquote(word) - if err == nil && word[0] == '\'' { - args[i] = []int(v)[0] - } else { - args[i], lerr = v, err - } - - case '.', '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - v, err := strconv.Btoi64(word, 0) - if err == nil { - args[i] = v - } else { - v, err := strconv.Atof64(word) - args[i], lerr = v, err - } - - default: - args[i] = fieldName(word) - } - if lerr != nil { - t.parseError("invalid literal: %q: %s", word, lerr) - } - } - - // We could remember the function address here and avoid the lookup later, - // but it's more dynamic to let the user change the map contents underfoot. - // We do require the name to be present, though. - - // Is it in user-supplied map? - for _, f := range formatters { - if t.formatter(f) == nil { - t.parseError("unknown formatter: %q", f) - } - } - - return &variableElement{t.linenum, args, formatters} -} - -// extractFormatters extracts a list of formatters from words. -// After the final space-separated argument in a variable, formatters may be -// specified separated by pipe symbols. For example: {a b c|d|e} -// The words parameter still has the formatters joined by '|' in the last word. -// extractFormatters splits formatters, replaces the last word with the content -// found before the first '|' within it, and returns the formatters obtained. -// If no formatters are found in words, the default formatter is returned. -func extractFormatters(words []string) (formatters []string) { - // "" is the default formatter. - formatters = []string{""} - if len(words) == 0 { - return - } - var bar int - lastWord := words[len(words)-1] - if isQuote(lastWord[0]) { - end := endQuote([]byte(lastWord), 0) - if end < 0 || end+1 == len(lastWord) || lastWord[end+1] != '|' { - return - } - bar = end + 1 - } else { - bar = strings.IndexRune(lastWord, '|') - if bar < 0 { - return - } - } - words[len(words)-1] = lastWord[0:bar] - formatters = strings.Split(lastWord[bar+1:], "|") - return -} - -// Grab the next item. If it's simple, just append it to the template. -// Otherwise return its details. -func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { - tok, w = t.analyze(item) - done = true // assume for simplicity - switch tok { - case tokComment: - return - case tokText: - t.elems = append(t.elems, &textElement{item}) - return - case tokLiteral: - switch w[0] { - case ".meta-left": - t.elems = append(t.elems, &literalElement{t.ldelim}) - case ".meta-right": - t.elems = append(t.elems, &literalElement{t.rdelim}) - case ".space": - t.elems = append(t.elems, &literalElement{space}) - case ".tab": - t.elems = append(t.elems, &literalElement{tab}) - default: - t.parseError("internal error: unknown literal: %s", w[0]) - } - return - case tokVariable: - t.elems = append(t.elems, t.newVariable(w)) - return - } - return false, tok, w -} - -// parseRepeated and parseSection are mutually recursive - -func (t *Template) parseRepeated(words []string) *repeatedElement { - r := new(repeatedElement) - t.elems = append(t.elems, r) - r.linenum = t.linenum - r.field = words[2] - // Scan section, collecting true and false (.or) blocks. - r.start = len(t.elems) - r.or = -1 - r.altstart = -1 - r.altend = -1 -Loop: - for { - item := t.nextItem() - if len(item) == 0 { - t.parseError("missing .end for .repeated section") - break - } - done, tok, w := t.parseSimple(item) - if done { - continue - } - switch tok { - case tokEnd: - break Loop - case tokOr: - if r.or >= 0 { - t.parseError("extra .or in .repeated section") - break Loop - } - r.altend = len(t.elems) - r.or = len(t.elems) - case tokSection: - t.parseSection(w) - case tokRepeated: - t.parseRepeated(w) - case tokAlternates: - if r.altstart >= 0 { - t.parseError("extra .alternates in .repeated section") - break Loop - } - if r.or >= 0 { - t.parseError(".alternates inside .or block in .repeated section") - break Loop - } - r.altstart = len(t.elems) - default: - t.parseError("internal error: unknown repeated section item: %s", item) - break Loop - } - } - if r.altend < 0 { - r.altend = len(t.elems) - } - r.end = len(t.elems) - return r -} - -func (t *Template) parseSection(words []string) *sectionElement { - s := new(sectionElement) - t.elems = append(t.elems, s) - s.linenum = t.linenum - s.field = words[1] - // Scan section, collecting true and false (.or) blocks. - s.start = len(t.elems) - s.or = -1 -Loop: - for { - item := t.nextItem() - if len(item) == 0 { - t.parseError("missing .end for .section") - break - } - done, tok, w := t.parseSimple(item) - if done { - continue - } - switch tok { - case tokEnd: - break Loop - case tokOr: - if s.or >= 0 { - t.parseError("extra .or in .section") - break Loop - } - s.or = len(t.elems) - case tokSection: - t.parseSection(w) - case tokRepeated: - t.parseRepeated(w) - case tokAlternates: - t.parseError(".alternates not in .repeated") - default: - t.parseError("internal error: unknown section item: %s", item) - } - } - s.end = len(t.elems) - return s -} - -func (t *Template) parse() { - for { - item := t.nextItem() - if len(item) == 0 { - break - } - done, tok, w := t.parseSimple(item) - if done { - continue - } - switch tok { - case tokOr, tokEnd, tokAlternates: - t.parseError("unexpected %s", w[0]) - case tokSection: - t.parseSection(w) - case tokRepeated: - t.parseRepeated(w) - default: - t.parseError("internal error: bad directive in parse: %s", item) - } - } -} - -// -- Execution - -// -- Public interface - -// Parse initializes a Template by parsing its definition. The string -// s contains the template text. If any errors occur, Parse returns -// the error. -func (t *Template) Parse(s string) (err os.Error) { - if t.elems == nil { - return &Error{1, "template not allocated with New"} - } - if !validDelim(t.ldelim) || !validDelim(t.rdelim) { - return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)} - } - defer checkError(&err) - t.buf = []byte(s) - t.p = 0 - t.linenum = 1 - t.parse() - return nil +// Funcs adds the elements of the argument map to the template's function +// map. It panics if a value in the map is not a function with appropriate +// return type. +// The return value is the template, so calls can be chained. +func (t *Template) Funcs(funcMap FuncMap) *Template { + addValueFuncs(t.execFuncs, funcMap) + addFuncs(t.parseFuncs, funcMap) + return t } -// ParseFile is like Parse but reads the template definition from the -// named file. -func (t *Template) ParseFile(filename string) (err os.Error) { - b, err := ioutil.ReadFile(filename) +// Parse parses the template definition string to construct an internal +// representation of the template for execution. +func (t *Template) Parse(s string) (tmpl *Template, err os.Error) { + t.Tree, err = parse.New(t.name).Parse(s, t.parseFuncs, builtins) if err != nil { - return err + return nil, err } - return t.Parse(string(b)) -} - -// Execute applies a parsed template to the specified data object, -// generating output to wr. -func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) { - // Extract the driver data. - val := reflect.ValueOf(data) - defer checkError(&err) - t.p = 0 - t.execute(0, len(t.elems), &state{parent: nil, data: val, wr: wr}) - return nil -} - -// SetDelims sets the left and right delimiters for operations in the -// template. They are validated during parsing. They could be -// validated here but it's better to keep the routine simple. The -// delimiters are very rarely invalid and Parse has the necessary -// error-handling interface already. -func (t *Template) SetDelims(left, right string) { - t.ldelim = []byte(left) - t.rdelim = []byte(right) + return t, nil } -// Parse creates a Template with default parameters (such as {} for -// metacharacters). The string s contains the template text while -// the formatter map fmap, which may be nil, defines auxiliary functions -// for formatting variables. The template is returned. If any errors -// occur, err will be non-nil. -func Parse(s string, fmap FormatterMap) (t *Template, err os.Error) { - t = New(fmap) - err = t.Parse(s) - if err != nil { - t = nil +// ParseInSet parses the template definition string to construct an internal +// representation of the template for execution. It also adds the template +// to the set. +// Function bindings are checked against those in the set. +func (t *Template) ParseInSet(s string, set *Set) (tmpl *Template, err os.Error) { + var setFuncs FuncMap + if set != nil { + setFuncs = set.parseFuncs } - return -} - -// ParseFile is a wrapper function that creates a Template with default -// parameters (such as {} for metacharacters). The filename identifies -// a file containing the template text, while the formatter map fmap, which -// may be nil, defines auxiliary functions for formatting variables. -// The template is returned. If any errors occur, err will be non-nil. -func ParseFile(filename string, fmap FormatterMap) (t *Template, err os.Error) { - b, err := ioutil.ReadFile(filename) + t.Tree, err = parse.New(t.name).Parse(s, t.parseFuncs, setFuncs, builtins) if err != nil { return nil, err } - return Parse(string(b), fmap) + t.addToSet(set) + return t, nil } -// MustParse is like Parse but panics if the template cannot be parsed. -func MustParse(s string, fmap FormatterMap) *Template { - t, err := Parse(s, fmap) - if err != nil { - panic("template.MustParse error: " + err.String()) - } - return t -} - -// MustParseFile is like ParseFile but panics if the file cannot be read -// or the template cannot be parsed. -func MustParseFile(filename string, fmap FormatterMap) *Template { - b, err := ioutil.ReadFile(filename) - if err != nil { - panic("template.MustParseFile error: " + err.String()) +// addToSet adds the template to the set, verifying it's not being double-assigned. +func (t *Template) addToSet(set *Set) { + if set == nil || t.set == set { + return } - return MustParse(string(b), fmap) + // If double-assigned, Add will panic and we will turn that into an error. + set.Add(t) } diff --git a/src/pkg/template/parse/Makefile b/src/pkg/template/parse/Makefile new file mode 100644 index 000000000..fe6585809 --- /dev/null +++ b/src/pkg/template/parse/Makefile @@ -0,0 +1,14 @@ +# 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. + +include ../../../Make.inc + +TARG=template/parse +GOFILES=\ + lex.go\ + node.go\ + parse.go\ + set.go\ + +include ../../../Make.pkg diff --git a/src/pkg/template/parse/lex.go b/src/pkg/template/parse/lex.go new file mode 100644 index 000000000..7ec4e920b --- /dev/null +++ b/src/pkg/template/parse/lex.go @@ -0,0 +1,474 @@ +// 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 parse + +import ( + "fmt" + "strings" + "unicode" + "utf8" +) + +// item represents a token or text string returned from the scanner. +type item struct { + typ itemType + val string +} + +func (i item) String() string { + switch { + case i.typ == itemEOF: + return "EOF" + case i.typ == itemError: + return i.val + case i.typ > itemKeyword: + return fmt.Sprintf("<%s>", i.val) + case len(i.val) > 10: + return fmt.Sprintf("%.10q...", i.val) + } + return fmt.Sprintf("%q", i.val) +} + +// itemType identifies the type of lex items. +type itemType int + +const ( + itemError itemType = iota // error occurred; value is text of error + itemBool // boolean constant + itemChar // printable ASCII character; grab bag for comma etc. + itemCharConstant // character constant + itemComplex // complex constant (1+2i); imaginary is just a number + itemColonEquals // colon-equals (':=') introducing a declaration + itemEOF + itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y') + itemIdentifier // alphanumeric identifier + itemLeftDelim // left action delimiter + itemNumber // simple number, including imaginary + itemPipe // pipe symbol + itemRawString // raw quoted string (includes quotes) + itemRightDelim // right action delimiter + itemString // quoted string (includes quotes) + itemText // plain text + itemVariable // variable starting with '$', such as '$' or '$1' or '$hello'. + // Keywords appear after all the rest. + itemKeyword // used only to delimit the keywords + itemDot // the cursor, spelled '.'. + itemDefine // define keyword + itemElse // else keyword + itemEnd // end keyword + itemIf // if keyword + itemRange // range keyword + itemTemplate // template keyword + itemWith // with keyword +) + +// Make the types prettyprint. +var itemName = map[itemType]string{ + itemError: "error", + itemBool: "bool", + itemChar: "char", + itemCharConstant: "charconst", + itemComplex: "complex", + itemColonEquals: ":=", + itemEOF: "EOF", + itemField: "field", + itemIdentifier: "identifier", + itemLeftDelim: "left delim", + itemNumber: "number", + itemPipe: "pipe", + itemRawString: "raw string", + itemRightDelim: "right delim", + itemString: "string", + itemVariable: "variable", + // keywords + itemDot: ".", + itemDefine: "define", + itemElse: "else", + itemIf: "if", + itemEnd: "end", + itemRange: "range", + itemTemplate: "template", + itemWith: "with", +} + +func (i itemType) String() string { + s := itemName[i] + if s == "" { + return fmt.Sprintf("item%d", int(i)) + } + return s +} + +var key = map[string]itemType{ + ".": itemDot, + "define": itemDefine, + "else": itemElse, + "end": itemEnd, + "if": itemIf, + "range": itemRange, + "template": itemTemplate, + "with": itemWith, +} + +const eof = -1 + +// stateFn represents the state of the scanner as a function that returns the next state. +type stateFn func(*lexer) stateFn + +// lexer holds the state of the scanner. +type lexer struct { + name string // the name of the input; used only for error reports. + input string // the string being scanned. + state stateFn // the next lexing function to enter + pos int // current position in the input. + start int // start position of this item. + width int // width of last rune read from input. + items chan item // channel of scanned items. +} + +// next returns the next rune in the input. +func (l *lexer) next() (rune int) { + if l.pos >= len(l.input) { + l.width = 0 + return eof + } + rune, l.width = utf8.DecodeRuneInString(l.input[l.pos:]) + l.pos += l.width + return rune +} + +// peek returns but does not consume the next rune in the input. +func (l *lexer) peek() int { + rune := l.next() + l.backup() + return rune +} + +// backup steps back one rune. Can only be called once per call of next. +func (l *lexer) backup() { + l.pos -= l.width +} + +// emit passes an item back to the client. +func (l *lexer) emit(t itemType) { + l.items <- item{t, l.input[l.start:l.pos]} + l.start = l.pos +} + +// ignore skips over the pending input before this point. +func (l *lexer) ignore() { + l.start = l.pos +} + +// accept consumes the next rune if it's from the valid set. +func (l *lexer) accept(valid string) bool { + if strings.IndexRune(valid, l.next()) >= 0 { + return true + } + l.backup() + return false +} + +// acceptRun consumes a run of runes from the valid set. +func (l *lexer) acceptRun(valid string) { + for strings.IndexRune(valid, l.next()) >= 0 { + } + l.backup() +} + +// lineNumber reports which line we're on. Doing it this way +// means we don't have to worry about peek double counting. +func (l *lexer) lineNumber() int { + return 1 + strings.Count(l.input[:l.pos], "\n") +} + +// error returns an error token and terminates the scan by passing +// back a nil pointer that will be the next state, terminating l.run. +func (l *lexer) errorf(format string, args ...interface{}) stateFn { + l.items <- item{itemError, fmt.Sprintf(format, args...)} + return nil +} + +// nextItem returns the next item from the input. +func (l *lexer) nextItem() item { + for { + select { + case item := <-l.items: + return item + default: + l.state = l.state(l) + } + } + panic("not reached") +} + +// lex creates a new scanner for the input string. +func lex(name, input string) *lexer { + l := &lexer{ + name: name, + input: input, + state: lexText, + items: make(chan item, 2), // Two items of buffering is sufficient for all state functions + } + return l +} + +// state functions + +const ( + leftDelim = "{{" + rightDelim = "}}" + leftComment = "{{/*" + rightComment = "*/}}" +) + +// lexText scans until an opening action delimiter, "{{". +func lexText(l *lexer) stateFn { + for { + if strings.HasPrefix(l.input[l.pos:], leftDelim) { + if l.pos > l.start { + l.emit(itemText) + } + return lexLeftDelim + } + if l.next() == eof { + break + } + } + // Correctly reached EOF. + if l.pos > l.start { + l.emit(itemText) + } + l.emit(itemEOF) + return nil +} + +// lexLeftDelim scans the left delimiter, which is known to be present. +func lexLeftDelim(l *lexer) stateFn { + if strings.HasPrefix(l.input[l.pos:], leftComment) { + return lexComment + } + l.pos += len(leftDelim) + l.emit(itemLeftDelim) + return lexInsideAction +} + +// lexComment scans a comment. The left comment marker is known to be present. +func lexComment(l *lexer) stateFn { + i := strings.Index(l.input[l.pos:], rightComment) + if i < 0 { + return l.errorf("unclosed comment") + } + l.pos += i + len(rightComment) + l.ignore() + return lexText +} + +// lexRightDelim scans the right delimiter, which is known to be present. +func lexRightDelim(l *lexer) stateFn { + l.pos += len(rightDelim) + l.emit(itemRightDelim) + return lexText +} + +// lexInsideAction scans the elements inside action delimiters. +func lexInsideAction(l *lexer) stateFn { + // Either number, quoted string, or identifier. + // Spaces separate and are ignored. + // Pipe symbols separate and are emitted. + for { + if strings.HasPrefix(l.input[l.pos:], rightDelim) { + return lexRightDelim + } + switch r := l.next(); { + case r == eof || r == '\n': + return l.errorf("unclosed action") + case isSpace(r): + l.ignore() + case r == ':': + if l.next() != '=' { + return l.errorf("expected :=") + } + l.emit(itemColonEquals) + case r == '|': + l.emit(itemPipe) + case r == '"': + return lexQuote + case r == '`': + return lexRawQuote + case r == '$': + return lexIdentifier + case r == '\'': + return lexChar + case r == '.': + // special look-ahead for ".field" so we don't break l.backup(). + if l.pos < len(l.input) { + r := l.input[l.pos] + if r < '0' || '9' < r { + return lexIdentifier // itemDot comes from the keyword table. + } + } + fallthrough // '.' can start a number. + case r == '+' || r == '-' || ('0' <= r && r <= '9'): + l.backup() + return lexNumber + case isAlphaNumeric(r): + l.backup() + return lexIdentifier + case r <= unicode.MaxASCII && unicode.IsPrint(r): + l.emit(itemChar) + return lexInsideAction + default: + return l.errorf("unrecognized character in action: %#U", r) + } + } + return nil +} + +// lexIdentifier scans an alphanumeric or field. +func lexIdentifier(l *lexer) stateFn { +Loop: + for { + switch r := l.next(); { + case isAlphaNumeric(r): + // absorb. + case r == '.' && (l.input[l.start] == '.' || l.input[l.start] == '$'): + // field chaining; absorb into one token. + default: + l.backup() + word := l.input[l.start:l.pos] + switch { + case key[word] > itemKeyword: + l.emit(key[word]) + case word[0] == '.': + l.emit(itemField) + case word[0] == '$': + l.emit(itemVariable) + case word == "true", word == "false": + l.emit(itemBool) + default: + l.emit(itemIdentifier) + } + break Loop + } + } + return lexInsideAction +} + +// lexChar scans a character constant. The initial quote is already +// scanned. Syntax checking is done by the parse. +func lexChar(l *lexer) stateFn { +Loop: + for { + switch l.next() { + case '\\': + if r := l.next(); r != eof && r != '\n' { + break + } + fallthrough + case eof, '\n': + return l.errorf("unterminated character constant") + case '\'': + break Loop + } + } + l.emit(itemCharConstant) + return lexInsideAction +} + +// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This +// isn't a perfect number scanner - for instance it accepts "." and "0x0.2" +// and "089" - but when it's wrong the input is invalid and the parser (via +// strconv) will notice. +func lexNumber(l *lexer) stateFn { + if !l.scanNumber() { + return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) + } + if sign := l.peek(); sign == '+' || sign == '-' { + // Complex: 1+2i. No spaces, must end in 'i'. + if !l.scanNumber() || l.input[l.pos-1] != 'i' { + return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) + } + l.emit(itemComplex) + } else { + l.emit(itemNumber) + } + return lexInsideAction +} + +func (l *lexer) scanNumber() bool { + // Optional leading sign. + l.accept("+-") + // Is it hex? + digits := "0123456789" + if l.accept("0") && l.accept("xX") { + digits = "0123456789abcdefABCDEF" + } + l.acceptRun(digits) + if l.accept(".") { + l.acceptRun(digits) + } + if l.accept("eE") { + l.accept("+-") + l.acceptRun("0123456789") + } + // Is it imaginary? + l.accept("i") + // Next thing mustn't be alphanumeric. + if isAlphaNumeric(l.peek()) { + l.next() + return false + } + return true +} + +// lexQuote scans a quoted string. +func lexQuote(l *lexer) stateFn { +Loop: + for { + switch l.next() { + case '\\': + if r := l.next(); r != eof && r != '\n' { + break + } + fallthrough + case eof, '\n': + return l.errorf("unterminated quoted string") + case '"': + break Loop + } + } + l.emit(itemString) + return lexInsideAction +} + +// lexRawQuote scans a raw quoted string. +func lexRawQuote(l *lexer) stateFn { +Loop: + for { + switch l.next() { + case eof, '\n': + return l.errorf("unterminated raw quoted string") + case '`': + break Loop + } + } + l.emit(itemRawString) + return lexInsideAction +} + +// isSpace reports whether r is a space character. +func isSpace(r int) bool { + switch r { + case ' ', '\t', '\n', '\r': + return true + } + return false +} + +// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. +func isAlphaNumeric(r int) bool { + return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) +} diff --git a/src/pkg/template/parse/lex_test.go b/src/pkg/template/parse/lex_test.go new file mode 100644 index 000000000..2ad91d5fa --- /dev/null +++ b/src/pkg/template/parse/lex_test.go @@ -0,0 +1,209 @@ +// 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 parse + +import ( + "reflect" + "testing" +) + +type lexTest struct { + name string + input string + items []item +} + +var ( + tEOF = item{itemEOF, ""} + tLeft = item{itemLeftDelim, "{{"} + tRight = item{itemRightDelim, "}}"} + tRange = item{itemRange, "range"} + tPipe = item{itemPipe, "|"} + tFor = item{itemIdentifier, "for"} + tQuote = item{itemString, `"abc \n\t\" "`} + raw = "`" + `abc\n\t\" ` + "`" + tRawQuote = item{itemRawString, raw} +) + +var lexTests = []lexTest{ + {"empty", "", []item{tEOF}}, + {"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}}, + {"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}}, + {"text with comment", "hello-{{/* this is a comment */}}-world", []item{ + {itemText, "hello-"}, + {itemText, "-world"}, + tEOF, + }}, + {"punctuation", "{{,@%}}", []item{ + tLeft, + {itemChar, ","}, + {itemChar, "@"}, + {itemChar, "%"}, + tRight, + tEOF, + }}, + {"empty action", `{{}}`, []item{tLeft, tRight, tEOF}}, + {"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}}, + {"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}}, + {"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}}, + {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{ + tLeft, + {itemNumber, "1"}, + {itemNumber, "02"}, + {itemNumber, "0x14"}, + {itemNumber, "-7.2i"}, + {itemNumber, "1e3"}, + {itemNumber, "+1.2e-4"}, + {itemNumber, "4.2i"}, + {itemComplex, "1+2i"}, + tRight, + tEOF, + }}, + {"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{ + tLeft, + {itemCharConstant, `'a'`}, + {itemCharConstant, `'\n'`}, + {itemCharConstant, `'\''`}, + {itemCharConstant, `'\\'`}, + {itemCharConstant, `'\u00FF'`}, + {itemCharConstant, `'\xFF'`}, + {itemCharConstant, `'本'`}, + tRight, + tEOF, + }}, + {"bools", "{{true false}}", []item{ + tLeft, + {itemBool, "true"}, + {itemBool, "false"}, + tRight, + tEOF, + }}, + {"dot", "{{.}}", []item{ + tLeft, + {itemDot, "."}, + tRight, + tEOF, + }}, + {"dots", "{{.x . .2 .x.y}}", []item{ + tLeft, + {itemField, ".x"}, + {itemDot, "."}, + {itemNumber, ".2"}, + {itemField, ".x.y"}, + tRight, + tEOF, + }}, + {"keywords", "{{range if else end with}}", []item{ + tLeft, + {itemRange, "range"}, + {itemIf, "if"}, + {itemElse, "else"}, + {itemEnd, "end"}, + {itemWith, "with"}, + tRight, + tEOF, + }}, + {"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{ + tLeft, + {itemVariable, "$c"}, + {itemColonEquals, ":="}, + {itemIdentifier, "printf"}, + {itemVariable, "$"}, + {itemVariable, "$hello"}, + {itemVariable, "$23"}, + {itemVariable, "$"}, + {itemVariable, "$var.Field"}, + {itemField, ".Method"}, + tRight, + tEOF, + }}, + {"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{ + {itemText, "intro "}, + tLeft, + {itemIdentifier, "echo"}, + {itemIdentifier, "hi"}, + {itemNumber, "1.2"}, + tPipe, + {itemIdentifier, "noargs"}, + tPipe, + {itemIdentifier, "args"}, + {itemNumber, "1"}, + {itemString, `"hi"`}, + tRight, + {itemText, " outro"}, + tEOF, + }}, + {"declaration", "{{$v := 3}}", []item{ + tLeft, + {itemVariable, "$v"}, + {itemColonEquals, ":="}, + {itemNumber, "3"}, + tRight, + tEOF, + }}, + {"2 declarations", "{{$v , $w := 3}}", []item{ + tLeft, + {itemVariable, "$v"}, + {itemChar, ","}, + {itemVariable, "$w"}, + {itemColonEquals, ":="}, + {itemNumber, "3"}, + tRight, + tEOF, + }}, + // errors + {"badchar", "#{{\x01}}", []item{ + {itemText, "#"}, + tLeft, + {itemError, "unrecognized character in action: U+0001"}, + }}, + {"unclosed action", "{{\n}}", []item{ + tLeft, + {itemError, "unclosed action"}, + }}, + {"EOF in action", "{{range", []item{ + tLeft, + tRange, + {itemError, "unclosed action"}, + }}, + {"unclosed quote", "{{\"\n\"}}", []item{ + tLeft, + {itemError, "unterminated quoted string"}, + }}, + {"unclosed raw quote", "{{`xx\n`}}", []item{ + tLeft, + {itemError, "unterminated raw quoted string"}, + }}, + {"unclosed char constant", "{{'\n}}", []item{ + tLeft, + {itemError, "unterminated character constant"}, + }}, + {"bad number", "{{3k}}", []item{ + tLeft, + {itemError, `bad number syntax: "3k"`}, + }}, +} + +// collect gathers the emitted items into a slice. +func collect(t *lexTest) (items []item) { + l := lex(t.name, t.input) + for { + item := l.nextItem() + items = append(items, item) + if item.typ == itemEOF || item.typ == itemError { + break + } + } + return +} + +func TestLex(t *testing.T) { + for _, test := range lexTests { + items := collect(&test) + if !reflect.DeepEqual(items, test.items) { + t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) + } + } +} diff --git a/src/pkg/template/parse/node.go b/src/pkg/template/parse/node.go new file mode 100644 index 000000000..a917418dc --- /dev/null +++ b/src/pkg/template/parse/node.go @@ -0,0 +1,469 @@ +// 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. + +// Parse nodes. + +package parse + +import ( + "bytes" + "fmt" + "os" + "strconv" + "strings" +) + +// A node is an element in the parse tree. The interface is trivial. +type Node interface { + Type() NodeType + String() string +} + +// NodeType identifies the type of a parse tree node. +type NodeType int + +// Type returns itself and provides an easy default implementation +// for embedding in a Node. Embedded in all non-trivial Nodes. +func (t NodeType) Type() NodeType { + return t +} + +const ( + NodeText NodeType = iota // Plain text. + NodeAction // A simple action such as field evaluation. + NodeBool // A boolean constant. + NodeCommand // An element of a pipeline. + NodeDot // The cursor, dot. + NodeElse // An else action. + NodeEnd // An end action. + NodeField // A field or method name. + NodeIdentifier // An identifier; always a function name. + NodeIf // An if action. + NodeList // A list of Nodes. + NodeNumber // A numerical constant. + NodePipe // A pipeline of commands. + NodeRange // A range action. + NodeString // A string constant. + NodeTemplate // A template invocation action. + NodeVariable // A $ variable. + NodeWith // A with action. +) + +// Nodes. + +// ListNode holds a sequence of nodes. +type ListNode struct { + NodeType + Nodes []Node // The element nodes in lexical order. +} + +func newList() *ListNode { + return &ListNode{NodeType: NodeList} +} + +func (l *ListNode) append(n Node) { + l.Nodes = append(l.Nodes, n) +} + +func (l *ListNode) String() string { + b := new(bytes.Buffer) + fmt.Fprint(b, "[") + for _, n := range l.Nodes { + fmt.Fprint(b, n) + } + fmt.Fprint(b, "]") + return b.String() +} + +// TextNode holds plain text. +type TextNode struct { + NodeType + Text []byte // The text; may span newlines. +} + +func newText(text string) *TextNode { + return &TextNode{NodeType: NodeText, Text: []byte(text)} +} + +func (t *TextNode) String() string { + return fmt.Sprintf("(text: %q)", t.Text) +} + +// PipeNode holds a pipeline with optional declaration +type PipeNode struct { + NodeType + Line int // The line number in the input. + Decl []*VariableNode // Variable declarations in lexical order. + Cmds []*CommandNode // The commands in lexical order. +} + +func newPipeline(line int, decl []*VariableNode) *PipeNode { + return &PipeNode{NodeType: NodePipe, Line: line, Decl: decl} +} + +func (p *PipeNode) append(command *CommandNode) { + p.Cmds = append(p.Cmds, command) +} + +func (p *PipeNode) String() string { + if p.Decl != nil { + return fmt.Sprintf("%v := %v", p.Decl, p.Cmds) + } + return fmt.Sprintf("%v", p.Cmds) +} + +// ActionNode holds an action (something bounded by delimiters). +// Control actions have their own nodes; ActionNode represents simple +// ones such as field evaluations. +type ActionNode struct { + NodeType + Line int // The line number in the input. + Pipe *PipeNode // The pipeline in the action. +} + +func newAction(line int, pipe *PipeNode) *ActionNode { + return &ActionNode{NodeType: NodeAction, Line: line, Pipe: pipe} +} + +func (a *ActionNode) String() string { + return fmt.Sprintf("(action: %v)", a.Pipe) +} + +// CommandNode holds a command (a pipeline inside an evaluating action). +type CommandNode struct { + NodeType + Args []Node // Arguments in lexical order: Identifier, field, or constant. +} + +func newCommand() *CommandNode { + return &CommandNode{NodeType: NodeCommand} +} + +func (c *CommandNode) append(arg Node) { + c.Args = append(c.Args, arg) +} + +func (c *CommandNode) String() string { + return fmt.Sprintf("(command: %v)", c.Args) +} + +// IdentifierNode holds an identifier. +type IdentifierNode struct { + NodeType + Ident string // The identifier's name. +} + +// NewIdentifier returns a new IdentifierNode with the given identifier name. +func NewIdentifier(ident string) *IdentifierNode { + return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident} +} + +func (i *IdentifierNode) String() string { + return fmt.Sprintf("I=%s", i.Ident) +} + +// VariableNode holds a list of variable names. The dollar sign is +// part of the name. +type VariableNode struct { + NodeType + Ident []string // Variable names in lexical order. +} + +func newVariable(ident string) *VariableNode { + return &VariableNode{NodeType: NodeVariable, Ident: strings.Split(ident, ".")} +} + +func (v *VariableNode) String() string { + return fmt.Sprintf("V=%s", v.Ident) +} + +// DotNode holds the special identifier '.'. It is represented by a nil pointer. +type DotNode bool + +func newDot() *DotNode { + return nil +} + +func (d *DotNode) Type() NodeType { + return NodeDot +} + +func (d *DotNode) String() string { + return "{{<.>}}" +} + +// FieldNode holds a field (identifier starting with '.'). +// The names may be chained ('.x.y'). +// The period is dropped from each ident. +type FieldNode struct { + NodeType + Ident []string // The identifiers in lexical order. +} + +func newField(ident string) *FieldNode { + return &FieldNode{NodeType: NodeField, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period +} + +func (f *FieldNode) String() string { + return fmt.Sprintf("F=%s", f.Ident) +} + +// BoolNode holds a boolean constant. +type BoolNode struct { + NodeType + True bool // The value of the boolean constant. +} + +func newBool(true bool) *BoolNode { + return &BoolNode{NodeType: NodeBool, True: true} +} + +func (b *BoolNode) String() string { + return fmt.Sprintf("B=%t", b.True) +} + +// NumberNode holds a number: signed or unsigned integer, float, or complex. +// The value is parsed and stored under all the types that can represent the value. +// This simulates in a small amount of code the behavior of Go's ideal constants. +type NumberNode struct { + NodeType + IsInt bool // Number has an integral value. + IsUint bool // Number has an unsigned integral value. + IsFloat bool // Number has a floating-point value. + IsComplex bool // Number is complex. + Int64 int64 // The signed integer value. + Uint64 uint64 // The unsigned integer value. + Float64 float64 // The floating-point value. + Complex128 complex128 // The complex value. + Text string // The original textual representation from the input. +} + +func newNumber(text string, typ itemType) (*NumberNode, os.Error) { + n := &NumberNode{NodeType: NodeNumber, Text: text} + switch typ { + case itemCharConstant: + rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0]) + if err != nil { + return nil, err + } + if tail != "'" { + return nil, fmt.Errorf("malformed character constant: %s", text) + } + n.Int64 = int64(rune) + n.IsInt = true + n.Uint64 = uint64(rune) + n.IsUint = true + n.Float64 = float64(rune) // odd but those are the rules. + n.IsFloat = true + return n, nil + case itemComplex: + // fmt.Sscan can parse the pair, so let it do the work. + if _, err := fmt.Sscan(text, &n.Complex128); err != nil { + return nil, err + } + n.IsComplex = true + n.simplifyComplex() + return n, nil + } + // Imaginary constants can only be complex unless they are zero. + if len(text) > 0 && text[len(text)-1] == 'i' { + f, err := strconv.Atof64(text[:len(text)-1]) + if err == nil { + n.IsComplex = true + n.Complex128 = complex(0, f) + n.simplifyComplex() + return n, nil + } + } + // Do integer test first so we get 0x123 etc. + u, err := strconv.Btoui64(text, 0) // will fail for -0; fixed below. + if err == nil { + n.IsUint = true + n.Uint64 = u + } + i, err := strconv.Btoi64(text, 0) + if err == nil { + n.IsInt = true + n.Int64 = i + if i == 0 { + n.IsUint = true // in case of -0. + n.Uint64 = u + } + } + // If an integer extraction succeeded, promote the float. + if n.IsInt { + n.IsFloat = true + n.Float64 = float64(n.Int64) + } else if n.IsUint { + n.IsFloat = true + n.Float64 = float64(n.Uint64) + } else { + f, err := strconv.Atof64(text) + if err == nil { + n.IsFloat = true + n.Float64 = f + // If a floating-point extraction succeeded, extract the int if needed. + if !n.IsInt && float64(int64(f)) == f { + n.IsInt = true + n.Int64 = int64(f) + } + if !n.IsUint && float64(uint64(f)) == f { + n.IsUint = true + n.Uint64 = uint64(f) + } + } + } + if !n.IsInt && !n.IsUint && !n.IsFloat { + return nil, fmt.Errorf("illegal number syntax: %q", text) + } + return n, nil +} + +// simplifyComplex pulls out any other types that are represented by the complex number. +// These all require that the imaginary part be zero. +func (n *NumberNode) simplifyComplex() { + n.IsFloat = imag(n.Complex128) == 0 + if n.IsFloat { + n.Float64 = real(n.Complex128) + n.IsInt = float64(int64(n.Float64)) == n.Float64 + if n.IsInt { + n.Int64 = int64(n.Float64) + } + n.IsUint = float64(uint64(n.Float64)) == n.Float64 + if n.IsUint { + n.Uint64 = uint64(n.Float64) + } + } +} + +func (n *NumberNode) String() string { + return fmt.Sprintf("N=%s", n.Text) +} + +// StringNode holds a string constant. The value has been "unquoted". +type StringNode struct { + NodeType + Quoted string // The original text of the string, with quotes. + Text string // The string, after quote processing. +} + +func newString(orig, text string) *StringNode { + return &StringNode{NodeType: NodeString, Quoted: orig, Text: text} +} + +func (s *StringNode) String() string { + return fmt.Sprintf("S=%#q", s.Text) +} + +// EndNode represents an {{end}} action. It is represented by a nil pointer. +type EndNode bool + +func newEnd() *EndNode { + return nil +} + +func (e *EndNode) Type() NodeType { + return NodeEnd +} + +func (e *EndNode) String() string { + return "{{end}}" +} + +// ElseNode represents an {{else}} action. +type ElseNode struct { + NodeType + Line int // The line number in the input. +} + +func newElse(line int) *ElseNode { + return &ElseNode{NodeType: NodeElse, Line: line} +} + +func (e *ElseNode) Type() NodeType { + return NodeElse +} + +func (e *ElseNode) String() string { + return "{{else}}" +} + +// IfNode represents an {{if}} action and its commands. +type IfNode struct { + NodeType + Line int // The line number in the input. + Pipe *PipeNode // The pipeline to be evaluated. + List *ListNode // What to execute if the value is non-empty. + ElseList *ListNode // What to execute if the value is empty (nil if absent). +} + +func newIf(line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { + return &IfNode{NodeType: NodeIf, Line: line, Pipe: pipe, List: list, ElseList: elseList} +} + +func (i *IfNode) String() string { + if i.ElseList != nil { + return fmt.Sprintf("({{if %s}} %s {{else}} %s)", i.Pipe, i.List, i.ElseList) + } + return fmt.Sprintf("({{if %s}} %s)", i.Pipe, i.List) +} + +// RangeNode represents a {{range}} action and its commands. +type RangeNode struct { + NodeType + Line int // The line number in the input. + Pipe *PipeNode // The pipeline to be evaluated. + List *ListNode // What to execute if the value is non-empty. + ElseList *ListNode // What to execute if the value is empty (nil if absent). +} + +func newRange(line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { + return &RangeNode{NodeType: NodeRange, Line: line, Pipe: pipe, List: list, ElseList: elseList} +} + +func (r *RangeNode) String() string { + if r.ElseList != nil { + return fmt.Sprintf("({{range %s}} %s {{else}} %s)", r.Pipe, r.List, r.ElseList) + } + return fmt.Sprintf("({{range %s}} %s)", r.Pipe, r.List) +} + +// TemplateNode represents a {{template}} action. +type TemplateNode struct { + NodeType + Line int // The line number in the input. + Name string // The name of the template (unquoted). + Pipe *PipeNode // The command to evaluate as dot for the template. +} + +func newTemplate(line int, name string, pipe *PipeNode) *TemplateNode { + return &TemplateNode{NodeType: NodeTemplate, Line: line, Name: name, Pipe: pipe} +} + +func (t *TemplateNode) String() string { + if t.Pipe == nil { + return fmt.Sprintf("{{template %q}}", t.Name) + } + return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe) +} + +// WithNode represents a {{with}} action and its commands. +type WithNode struct { + NodeType + Line int // The line number in the input. + Pipe *PipeNode // The pipeline to be evaluated. + List *ListNode // What to execute if the value is non-empty. + ElseList *ListNode // What to execute if the value is empty (nil if absent). +} + +func newWith(line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { + return &WithNode{NodeType: NodeWith, Line: line, Pipe: pipe, List: list, ElseList: elseList} +} + +func (w *WithNode) String() string { + if w.ElseList != nil { + return fmt.Sprintf("({{with %s}} %s {{else}} %s)", w.Pipe, w.List, w.ElseList) + } + return fmt.Sprintf("({{with %s}} %s)", w.Pipe, w.List) +} diff --git a/src/pkg/template/parse/parse.go b/src/pkg/template/parse/parse.go new file mode 100644 index 000000000..9a411a3f3 --- /dev/null +++ b/src/pkg/template/parse/parse.go @@ -0,0 +1,436 @@ +// 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 parse builds parse trees for templates. The grammar is defined +// in the documents for the template package. +package parse + +import ( + "fmt" + "os" + "runtime" + "strconv" + "unicode" +) + +// Tree is the representation of a parsed template. +type Tree struct { + Name string // Name is the name of the template. + Root *ListNode // Root is the top-level root of the parse tree. + // Parsing only; cleared after parse. + funcs []map[string]interface{} + lex *lexer + token [2]item // two-token lookahead for parser. + peekCount int + vars []string // variables defined at the moment. +} + +// next returns the next token. +func (t *Tree) next() item { + if t.peekCount > 0 { + t.peekCount-- + } else { + t.token[0] = t.lex.nextItem() + } + return t.token[t.peekCount] +} + +// backup backs the input stream up one token. +func (t *Tree) backup() { + t.peekCount++ +} + +// backup2 backs the input stream up two tokens +func (t *Tree) backup2(t1 item) { + t.token[1] = t1 + t.peekCount = 2 +} + +// peek returns but does not consume the next token. +func (t *Tree) peek() item { + if t.peekCount > 0 { + return t.token[t.peekCount-1] + } + t.peekCount = 1 + t.token[0] = t.lex.nextItem() + return t.token[0] +} + +// Parsing. + +// New allocates a new template with the given name. +func New(name string, funcs ...map[string]interface{}) *Tree { + return &Tree{ + Name: name, + funcs: funcs, + } +} + +// errorf formats the error and terminates processing. +func (t *Tree) errorf(format string, args ...interface{}) { + t.Root = nil + format = fmt.Sprintf("template: %s:%d: %s", t.Name, t.lex.lineNumber(), format) + panic(fmt.Errorf(format, args...)) +} + +// error terminates processing. +func (t *Tree) error(err os.Error) { + t.errorf("%s", err) +} + +// expect consumes the next token and guarantees it has the required type. +func (t *Tree) expect(expected itemType, context string) item { + token := t.next() + if token.typ != expected { + t.errorf("expected %s in %s; got %s", expected, context, token) + } + return token +} + +// unexpected complains about the token and terminates processing. +func (t *Tree) unexpected(token item, context string) { + t.errorf("unexpected %s in %s", token, context) +} + +// recover is the handler that turns panics into returns from the top level of Parse. +func (t *Tree) recover(errp *os.Error) { + e := recover() + if e != nil { + if _, ok := e.(runtime.Error); ok { + panic(e) + } + if t != nil { + t.stopParse() + } + *errp = e.(os.Error) + } + return +} + +// startParse starts the template parsing from the lexer. +func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) { + t.Root = nil + t.lex = lex + t.vars = []string{"$"} + t.funcs = funcs +} + +// stopParse terminates parsing. +func (t *Tree) stopParse() { + t.lex = nil + t.vars = nil + t.funcs = nil +} + +// atEOF returns true if, possibly after spaces, we're at EOF. +func (t *Tree) atEOF() bool { + for { + token := t.peek() + switch token.typ { + case itemEOF: + return true + case itemText: + for _, r := range token.val { + if !unicode.IsSpace(r) { + return false + } + } + t.next() // skip spaces. + continue + } + break + } + return false +} + +// Parse parses the template definition string to construct an internal +// representation of the template for execution. +func (t *Tree) Parse(s string, funcs ...map[string]interface{}) (tree *Tree, err os.Error) { + defer t.recover(&err) + t.startParse(funcs, lex(t.Name, s)) + t.parse(true) + t.stopParse() + return t, nil +} + +// parse is the helper for Parse. +// It triggers an error if we expect EOF but don't reach it. +func (t *Tree) parse(toEOF bool) (next Node) { + t.Root, next = t.itemList(true) + if toEOF && next != nil { + t.errorf("unexpected %s", next) + } + return next +} + +// itemList: +// textOrAction* +// Terminates at EOF and at {{end}} or {{else}}, which is returned separately. +// The toEOF flag tells whether we expect to reach EOF. +func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) { + list = newList() + for t.peek().typ != itemEOF { + n := t.textOrAction() + switch n.Type() { + case NodeEnd, NodeElse: + return list, n + } + list.append(n) + } + if !toEOF { + t.unexpected(t.next(), "input") + } + return list, nil +} + +// textOrAction: +// text | action +func (t *Tree) textOrAction() Node { + switch token := t.next(); token.typ { + case itemText: + return newText(token.val) + case itemLeftDelim: + return t.action() + default: + t.unexpected(token, "input") + } + return nil +} + +// Action: +// control +// command ("|" command)* +// Left delim is past. Now get actions. +// First word could be a keyword such as range. +func (t *Tree) action() (n Node) { + switch token := t.next(); token.typ { + case itemElse: + return t.elseControl() + case itemEnd: + return t.endControl() + case itemIf: + return t.ifControl() + case itemRange: + return t.rangeControl() + case itemTemplate: + return t.templateControl() + case itemWith: + return t.withControl() + } + t.backup() + // Do not pop variables; they persist until "end". + return newAction(t.lex.lineNumber(), t.pipeline("command")) +} + +// Pipeline: +// field or command +// pipeline "|" pipeline +func (t *Tree) pipeline(context string) (pipe *PipeNode) { + var decl []*VariableNode + // Are there declarations? + for { + if v := t.peek(); v.typ == itemVariable { + t.next() + if next := t.peek(); next.typ == itemColonEquals || next.typ == itemChar { + t.next() + variable := newVariable(v.val) + if len(variable.Ident) != 1 { + t.errorf("illegal variable in declaration: %s", v.val) + } + decl = append(decl, variable) + t.vars = append(t.vars, v.val) + if next.typ == itemChar && next.val == "," { + if context == "range" && len(decl) < 2 { + continue + } + t.errorf("too many declarations in %s", context) + } + } else { + t.backup2(v) + } + } + break + } + pipe = newPipeline(t.lex.lineNumber(), decl) + for { + switch token := t.next(); token.typ { + case itemRightDelim: + if len(pipe.Cmds) == 0 { + t.errorf("missing value for %s", context) + } + return + case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier, + itemVariable, itemNumber, itemRawString, itemString: + t.backup() + pipe.append(t.command()) + default: + t.unexpected(token, context) + } + } + return +} + +func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list, elseList *ListNode) { + lineNum = t.lex.lineNumber() + defer t.popVars(len(t.vars)) + pipe = t.pipeline(context) + var next Node + list, next = t.itemList(false) + switch next.Type() { + case NodeEnd: //done + case NodeElse: + elseList, next = t.itemList(false) + if next.Type() != NodeEnd { + t.errorf("expected end; found %s", next) + } + elseList = elseList + } + return lineNum, pipe, list, elseList +} + +// If: +// {{if pipeline}} itemList {{end}} +// {{if pipeline}} itemList {{else}} itemList {{end}} +// If keyword is past. +func (t *Tree) ifControl() Node { + return newIf(t.parseControl("if")) +} + +// Range: +// {{range pipeline}} itemList {{end}} +// {{range pipeline}} itemList {{else}} itemList {{end}} +// Range keyword is past. +func (t *Tree) rangeControl() Node { + return newRange(t.parseControl("range")) +} + +// With: +// {{with pipeline}} itemList {{end}} +// {{with pipeline}} itemList {{else}} itemList {{end}} +// If keyword is past. +func (t *Tree) withControl() Node { + return newWith(t.parseControl("with")) +} + +// End: +// {{end}} +// End keyword is past. +func (t *Tree) endControl() Node { + t.expect(itemRightDelim, "end") + return newEnd() +} + +// Else: +// {{else}} +// Else keyword is past. +func (t *Tree) elseControl() Node { + t.expect(itemRightDelim, "else") + return newElse(t.lex.lineNumber()) +} + +// Template: +// {{template stringValue pipeline}} +// Template keyword is past. The name must be something that can evaluate +// to a string. +func (t *Tree) templateControl() Node { + var name string + switch token := t.next(); token.typ { + case itemString, itemRawString: + s, err := strconv.Unquote(token.val) + if err != nil { + t.error(err) + } + name = s + default: + t.unexpected(token, "template invocation") + } + var pipe *PipeNode + if t.next().typ != itemRightDelim { + t.backup() + // Do not pop variables; they persist until "end". + pipe = t.pipeline("template") + } + return newTemplate(t.lex.lineNumber(), name, pipe) +} + +// command: +// space-separated arguments up to a pipeline character or right delimiter. +// we consume the pipe character but leave the right delim to terminate the action. +func (t *Tree) command() *CommandNode { + cmd := newCommand() +Loop: + for { + switch token := t.next(); token.typ { + case itemRightDelim: + t.backup() + break Loop + case itemPipe: + break Loop + case itemError: + t.errorf("%s", token.val) + case itemIdentifier: + if !t.hasFunction(token.val) { + t.errorf("function %q not defined", token.val) + } + cmd.append(NewIdentifier(token.val)) + case itemDot: + cmd.append(newDot()) + case itemVariable: + cmd.append(t.useVar(token.val)) + case itemField: + cmd.append(newField(token.val)) + case itemBool: + cmd.append(newBool(token.val == "true")) + case itemCharConstant, itemComplex, itemNumber: + number, err := newNumber(token.val, token.typ) + if err != nil { + t.error(err) + } + cmd.append(number) + case itemString, itemRawString: + s, err := strconv.Unquote(token.val) + if err != nil { + t.error(err) + } + cmd.append(newString(token.val, s)) + default: + t.unexpected(token, "command") + } + } + if len(cmd.Args) == 0 { + t.errorf("empty command") + } + return cmd +} + +// hasFunction reports if a function name exists in the Tree's maps. +func (t *Tree) hasFunction(name string) bool { + for _, funcMap := range t.funcs { + if funcMap == nil { + continue + } + if funcMap[name] != nil { + return true + } + } + return false +} + +// popVars trims the variable list to the specified length +func (t *Tree) popVars(n int) { + t.vars = t.vars[:n] +} + +// useVar returns a node for a variable reference. It errors if the +// variable is not defined. +func (t *Tree) useVar(name string) Node { + v := newVariable(name) + for _, varName := range t.vars { + if varName == v.Ident[0] { + return v + } + } + t.errorf("undefined variable %q", v.Ident[0]) + return nil +} diff --git a/src/pkg/template/parse/parse_test.go b/src/pkg/template/parse/parse_test.go new file mode 100644 index 000000000..1928c319d --- /dev/null +++ b/src/pkg/template/parse/parse_test.go @@ -0,0 +1,259 @@ +// 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 parse + +import ( + "flag" + "fmt" + "testing" +) + +var debug = flag.Bool("debug", false, "show the errors produced by the tests") + +type numberTest struct { + text string + isInt bool + isUint bool + isFloat bool + isComplex bool + int64 + uint64 + float64 + complex128 +} + +var numberTests = []numberTest{ + // basics + {"0", true, true, true, false, 0, 0, 0, 0}, + {"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint. + {"73", true, true, true, false, 73, 73, 73, 0}, + {"073", true, true, true, false, 073, 073, 073, 0}, + {"0x73", true, true, true, false, 0x73, 0x73, 0x73, 0}, + {"-73", true, false, true, false, -73, 0, -73, 0}, + {"+73", true, false, true, false, 73, 0, 73, 0}, + {"100", true, true, true, false, 100, 100, 100, 0}, + {"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0}, + {"-1e9", true, false, true, false, -1e9, 0, -1e9, 0}, + {"-1.2", false, false, true, false, 0, 0, -1.2, 0}, + {"1e19", false, true, true, false, 0, 1e19, 1e19, 0}, + {"-1e19", false, false, true, false, 0, 0, -1e19, 0}, + {"4i", false, false, false, true, 0, 0, 0, 4i}, + {"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i}, + {"073i", false, false, false, true, 0, 0, 0, 73i}, // not octal! + // complex with 0 imaginary are float (and maybe integer) + {"0i", true, true, true, true, 0, 0, 0, 0}, + {"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2}, + {"-12+0i", true, false, true, true, -12, 0, -12, -12}, + {"13+0i", true, true, true, true, 13, 13, 13, 13}, + // funny bases + {"0123", true, true, true, false, 0123, 0123, 0123, 0}, + {"-0x0", true, true, true, false, 0, 0, 0, 0}, + {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0}, + // character constants + {`'a'`, true, true, true, false, 'a', 'a', 'a', 0}, + {`'\n'`, true, true, true, false, '\n', '\n', '\n', 0}, + {`'\\'`, true, true, true, false, '\\', '\\', '\\', 0}, + {`'\''`, true, true, true, false, '\'', '\'', '\'', 0}, + {`'\xFF'`, true, true, true, false, 0xFF, 0xFF, 0xFF, 0}, + {`'パ'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0}, + {`'\u30d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0}, + {`'\U000030d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0}, + // some broken syntax + {text: "+-2"}, + {text: "0x123."}, + {text: "1e."}, + {text: "0xi."}, + {text: "1+2."}, + {text: "'x"}, + {text: "'xx'"}, +} + +func TestNumberParse(t *testing.T) { + for _, test := range numberTests { + // If fmt.Sscan thinks it's complex, it's complex. We can't trust the output + // because imaginary comes out as a number. + var c complex128 + typ := itemNumber + if test.text[0] == '\'' { + typ = itemCharConstant + } else { + _, err := fmt.Sscan(test.text, &c) + if err == nil { + typ = itemComplex + } + } + n, err := newNumber(test.text, typ) + ok := test.isInt || test.isUint || test.isFloat || test.isComplex + if ok && err != nil { + t.Errorf("unexpected error for %q: %s", test.text, err) + continue + } + if !ok && err == nil { + t.Errorf("expected error for %q", test.text) + continue + } + if !ok { + if *debug { + fmt.Printf("%s\n\t%s\n", test.text, err) + } + continue + } + if n.IsComplex != test.isComplex { + t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex) + } + if test.isInt { + if !n.IsInt { + t.Errorf("expected integer for %q", test.text) + } + if n.Int64 != test.int64 { + t.Errorf("int64 for %q should be %d Is %d", test.text, test.int64, n.Int64) + } + } else if n.IsInt { + t.Errorf("did not expect integer for %q", test.text) + } + if test.isUint { + if !n.IsUint { + t.Errorf("expected unsigned integer for %q", test.text) + } + if n.Uint64 != test.uint64 { + t.Errorf("uint64 for %q should be %d Is %d", test.text, test.uint64, n.Uint64) + } + } else if n.IsUint { + t.Errorf("did not expect unsigned integer for %q", test.text) + } + if test.isFloat { + if !n.IsFloat { + t.Errorf("expected float for %q", test.text) + } + if n.Float64 != test.float64 { + t.Errorf("float64 for %q should be %g Is %g", test.text, test.float64, n.Float64) + } + } else if n.IsFloat { + t.Errorf("did not expect float for %q", test.text) + } + if test.isComplex { + if !n.IsComplex { + t.Errorf("expected complex for %q", test.text) + } + if n.Complex128 != test.complex128 { + t.Errorf("complex128 for %q should be %g Is %g", test.text, test.complex128, n.Complex128) + } + } else if n.IsComplex { + t.Errorf("did not expect complex for %q", test.text) + } + } +} + +type parseTest struct { + name string + input string + ok bool + result string +} + +const ( + noError = true + hasError = false +) + +var parseTests = []parseTest{ + {"empty", "", noError, + `[]`}, + {"comment", "{{/*\n\n\n*/}}", noError, + `[]`}, + {"spaces", " \t\n", noError, + `[(text: " \t\n")]`}, + {"text", "some text", noError, + `[(text: "some text")]`}, + {"emptyAction", "{{}}", hasError, + `[(action: [])]`}, + {"field", "{{.X}}", noError, + `[(action: [(command: [F=[X]])])]`}, + {"simple command", "{{printf}}", noError, + `[(action: [(command: [I=printf])])]`}, + {"$ invocation", "{{$}}", noError, + "[(action: [(command: [V=[$]])])]"}, + {"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError, + "[({{with [V=[$x]] := [(command: [N=3])]}} [(action: [(command: [V=[$x] N=23])])])]"}, + {"variable with fields", "{{$.I}}", noError, + "[(action: [(command: [V=[$ I]])])]"}, + {"multi-word command", "{{printf `%d` 23}}", noError, + "[(action: [(command: [I=printf S=`%d` N=23])])]"}, + {"pipeline", "{{.X|.Y}}", noError, + `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`}, + {"pipeline with decl", "{{$x := .X|.Y}}", noError, + `[(action: [V=[$x]] := [(command: [F=[X]]) (command: [F=[Y]])])]`}, + {"declaration", "{{.X|.Y}}", noError, + `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`}, + {"simple if", "{{if .X}}hello{{end}}", noError, + `[({{if [(command: [F=[X]])]}} [(text: "hello")])]`}, + {"if with else", "{{if .X}}true{{else}}false{{end}}", noError, + `[({{if [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, + {"simple range", "{{range .X}}hello{{end}}", noError, + `[({{range [(command: [F=[X]])]}} [(text: "hello")])]`}, + {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError, + `[({{range [(command: [F=[X Y Z]])]}} [(text: "hello")])]`}, + {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError, + `[({{range [(command: [F=[X]])]}} [(text: "hello")({{range [(command: [F=[Y]])]}} [(text: "goodbye")])])]`}, + {"range with else", "{{range .X}}true{{else}}false{{end}}", noError, + `[({{range [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, + {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError, + `[({{range [(command: [F=[X]]) (command: [F=[M]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, + {"range []int", "{{range .SI}}{{.}}{{end}}", noError, + `[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`}, + {"constants", "{{range .SI 1 -3.2i true false 'a'}}{{end}}", noError, + `[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false N='a'])]}} [])]`}, + {"template", "{{template `x`}}", noError, + `[{{template "x"}}]`}, + {"template with arg", "{{template `x` .Y}}", noError, + `[{{template "x" [(command: [F=[Y]])]}}]`}, + {"with", "{{with .X}}hello{{end}}", noError, + `[({{with [(command: [F=[X]])]}} [(text: "hello")])]`}, + {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError, + `[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`}, + // Errors. + {"unclosed action", "hello{{range", hasError, ""}, + {"unmatched end", "{{end}}", hasError, ""}, + {"missing end", "hello{{range .x}}", hasError, ""}, + {"missing end after else", "hello{{range .x}}{{else}}", hasError, ""}, + {"undefined function", "hello{{undefined}}", hasError, ""}, + {"undefined variable", "{{$x}}", hasError, ""}, + {"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""}, + {"variable undefined in template", "{{template $v}}", hasError, ""}, + {"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""}, + {"template with field ref", "{{template .X}}", hasError, ""}, + {"template with var", "{{template $v}}", hasError, ""}, + {"invalid punctuation", "{{printf 3, 4}}", hasError, ""}, + {"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""}, + {"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""}, +} + +var builtins = map[string]interface{}{ + "printf": fmt.Sprintf, +} + +func TestParse(t *testing.T) { + for _, test := range parseTests { + tmpl, err := New(test.name).Parse(test.input, builtins) + switch { + case err == nil && !test.ok: + t.Errorf("%q: expected error; got none", test.name) + continue + case err != nil && test.ok: + t.Errorf("%q: unexpected error: %v", test.name, err) + continue + case err != nil && !test.ok: + // expected error, got one + if *debug { + fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) + } + continue + } + result := tmpl.Root.String() + if result != test.result { + t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result) + } + } +} diff --git a/src/pkg/template/parse/set.go b/src/pkg/template/parse/set.go new file mode 100644 index 000000000..4820da925 --- /dev/null +++ b/src/pkg/template/parse/set.go @@ -0,0 +1,50 @@ +// 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 parse + +import ( + "fmt" + "os" + "strconv" +) + +// Set returns a slice of Trees created by parsing the template set +// definition in the argument string. If an error is encountered, +// parsing stops and an empty slice is returned with the error. +func Set(text string, funcs ...map[string]interface{}) (tree map[string]*Tree, err os.Error) { + tree = make(map[string]*Tree) + defer (*Tree)(nil).recover(&err) + lex := lex("set", text) + const context = "define clause" + for { + t := New("set") // name will be updated once we know it. + t.startParse(funcs, lex) + // Expect EOF or "{{ define name }}". + if t.atEOF() { + break + } + t.expect(itemLeftDelim, context) + t.expect(itemDefine, context) + name := t.expect(itemString, context) + t.Name, err = strconv.Unquote(name.val) + if err != nil { + t.error(err) + } + t.expect(itemRightDelim, context) + end := t.parse(false) + if end == nil { + t.errorf("unexpected EOF in %s", context) + } + if end.Type() != NodeEnd { + t.errorf("unexpected %s in %s", end, context) + } + t.stopParse() + if _, present := tree[t.Name]; present { + return nil, fmt.Errorf("template: %q multiply defined", name) + } + tree[t.Name] = t + } + return +} diff --git a/src/pkg/template/set.go b/src/pkg/template/set.go new file mode 100644 index 000000000..f778fd169 --- /dev/null +++ b/src/pkg/template/set.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 template + +import ( + "fmt" + "io" + "os" + "reflect" + "template/parse" +) + +// Set holds a set of related templates that can refer to one another by name. +// The zero value represents an empty set. +// A template may be a member of multiple sets. +type Set struct { + tmpl map[string]*Template + parseFuncs FuncMap + execFuncs map[string]reflect.Value +} + +func (s *Set) init() { + if s.tmpl == nil { + s.tmpl = make(map[string]*Template) + s.parseFuncs = make(FuncMap) + s.execFuncs = make(map[string]reflect.Value) + } +} + +// Funcs adds the elements of the argument map to the set's function map. It +// panics if a value in the map is not a function with appropriate return +// type. +// The return value is the set, so calls can be chained. +func (s *Set) Funcs(funcMap FuncMap) *Set { + s.init() + addValueFuncs(s.execFuncs, funcMap) + addFuncs(s.parseFuncs, funcMap) + return s +} + +// Add adds the argument templates to the set. It panics if two templates +// with the same name are added or if a template is already a member of +// a set. +// The return value is the set, so calls can be chained. +func (s *Set) Add(templates ...*Template) *Set { + for _, t := range templates { + if err := s.add(t); err != nil { + panic(err) + } + } + return s +} + +// add adds the argument template to the set. +func (s *Set) add(t *Template) os.Error { + s.init() + if t.set != nil { + return fmt.Errorf("template: %q already in a set", t.name) + } + if _, ok := s.tmpl[t.name]; ok { + return fmt.Errorf("template: %q already defined in set", t.name) + } + s.tmpl[t.name] = t + t.set = s + return nil +} + +// Template returns the template with the given name in the set, +// or nil if there is no such template. +func (s *Set) Template(name string) *Template { + return s.tmpl[name] +} + +// FuncMap returns the set's function map. +func (s *Set) FuncMap() FuncMap { + return s.parseFuncs +} + +// Execute applies the named template to the specified data object, writing +// the output to wr. +func (s *Set) Execute(wr io.Writer, name string, data interface{}) os.Error { + tmpl := s.tmpl[name] + if tmpl == nil { + return fmt.Errorf("template: no template %q in set", name) + } + return tmpl.Execute(wr, data) +} + +// Parse parses a string into a set of named templates. Parse may be called +// multiple times for a given set, adding the templates defined in the string +// to the set. If a template is redefined, the element in the set is +// overwritten with the new definition. +func (s *Set) Parse(text string) (*Set, os.Error) { + trees, err := parse.Set(text, s.parseFuncs, builtins) + if err != nil { + return nil, err + } + s.init() + for name, tree := range trees { + tmpl := New(name) + tmpl.Tree = tree + tmpl.addToSet(s) + s.tmpl[name] = tmpl + } + return s, nil +} diff --git a/src/pkg/template/set_test.go b/src/pkg/template/set_test.go new file mode 100644 index 000000000..f437bc779 --- /dev/null +++ b/src/pkg/template/set_test.go @@ -0,0 +1,239 @@ +// 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 template + +import ( + "fmt" + "testing" +) + +const ( + noError = true + hasError = false +) + +type setParseTest struct { + name string + input string + ok bool + names []string + results []string +} + +var setParseTests = []setParseTest{ + {"empty", "", noError, + nil, + nil}, + {"one", `{{define "foo"}} FOO {{end}}`, noError, + []string{"foo"}, + []string{`[(text: " FOO ")]`}}, + {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, + []string{"foo", "bar"}, + []string{`[(text: " FOO ")]`, `[(text: " BAR ")]`}}, + // errors + {"missing end", `{{define "foo"}} FOO `, hasError, + nil, + nil}, + {"malformed name", `{{define "foo}} FOO `, hasError, + nil, + nil}, +} + +func TestSetParse(t *testing.T) { + for _, test := range setParseTests { + set, err := new(Set).Parse(test.input) + switch { + case err == nil && !test.ok: + t.Errorf("%q: expected error; got none", test.name) + continue + case err != nil && test.ok: + t.Errorf("%q: unexpected error: %v", test.name, err) + continue + case err != nil && !test.ok: + // expected error, got one + if *debug { + fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) + } + continue + } + if set == nil { + continue + } + if len(set.tmpl) != len(test.names) { + t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(set.tmpl)) + continue + } + for i, name := range test.names { + tmpl, ok := set.tmpl[name] + if !ok { + t.Errorf("%s: can't find template %q", test.name, name) + continue + } + result := tmpl.Root.String() + if result != test.results[i] { + t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) + } + } + } +} + +var setExecTests = []execTest{ + {"empty", "", "", nil, true}, + {"text", "some text", "some text", nil, true}, + {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true}, + {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true}, + {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, + {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, + {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, + {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, + {"variable declared by template", `{{template "nested" $x=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true}, + + // User-defined function: test argument evaluator. + {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true}, + {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true}, +} + +// These strings are also in testdata/*. +const setText1 = ` + {{define "x"}}TEXT{{end}} + {{define "dotV"}}{{.V}}{{end}} +` + +const setText2 = ` + {{define "dot"}}{{.}}{{end}} + {{define "nested"}}{{template "dot" .}}{{end}} +` + +func TestSetExecute(t *testing.T) { + // Declare a set with a couple of templates first. + set := new(Set) + _, err := set.Parse(setText1) + if err != nil { + t.Fatalf("error parsing set: %s", err) + } + _, err = set.Parse(setText2) + if err != nil { + t.Fatalf("error parsing set: %s", err) + } + testExecute(setExecTests, set, t) +} + +func TestSetParseFiles(t *testing.T) { + set := new(Set) + _, err := set.ParseFiles("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + _, err = set.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(setExecTests, set, t) +} + +func TestParseSetFiles(t *testing.T) { + set := new(Set) + _, err := ParseSetFiles("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + set, err = ParseSetFiles("testdata/file1.tmpl", "testdata/file2.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(setExecTests, set, t) +} + +func TestSetParseGlob(t *testing.T) { + _, err := new(Set).ParseGlob("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + _, err = new(Set).ParseGlob("[x") + if err == nil { + t.Error("expected error for bad pattern; got none") + } + set, err := new(Set).ParseGlob("testdata/file*.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(setExecTests, set, t) +} + +func TestParseSetGlob(t *testing.T) { + _, err := ParseSetGlob("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + _, err = ParseSetGlob("[x") + if err == nil { + t.Error("expected error for bad pattern; got none") + } + set, err := ParseSetGlob("testdata/file*.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(setExecTests, set, t) +} + +var templateFileExecTests = []execTest{ + {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\ntemplate2\n", 0, true}, +} + +func TestSetParseTemplateFiles(t *testing.T) { + _, err := ParseTemplateFiles("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + set, err := new(Set).ParseTemplateFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(templateFileExecTests, set, t) +} + +func TestParseTemplateFiles(t *testing.T) { + _, err := ParseTemplateFiles("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + set, err := new(Set).ParseTemplateFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(templateFileExecTests, set, t) +} + +func TestSetParseTemplateGlob(t *testing.T) { + _, err := ParseTemplateGlob("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + _, err = new(Set).ParseTemplateGlob("[x") + if err == nil { + t.Error("expected error for bad pattern; got none") + } + set, err := new(Set).ParseTemplateGlob("testdata/tmpl*.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(templateFileExecTests, set, t) +} + +func TestParseTemplateGlob(t *testing.T) { + _, err := ParseTemplateGlob("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + _, err = ParseTemplateGlob("[x") + if err == nil { + t.Error("expected error for bad pattern; got none") + } + set, err := ParseTemplateGlob("testdata/tmpl*.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(templateFileExecTests, set, t) +} diff --git a/src/pkg/template/template_test.go b/src/pkg/template/template_test.go deleted file mode 100644 index eae8011eb..000000000 --- a/src/pkg/template/template_test.go +++ /dev/null @@ -1,804 +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 template - -import ( - "bytes" - "container/vector" - "fmt" - "io" - "io/ioutil" - "json" - "os" - "strings" - "testing" -) - -type Test struct { - in, out, err string -} - -type T struct { - Item string - Value string -} - -type U struct { - Mp map[string]int -} - -type S struct { - Header string - HeaderPtr *string - Integer int - IntegerPtr *int - NilPtr *int - InnerT T - InnerPointerT *T - Data []T - Pdata []*T - Empty []*T - Emptystring string - Null []*T - Vec *vector.Vector - True bool - False bool - Mp map[string]string - JSON interface{} - Innermap U - Stringmap map[string]string - Ptrmap map[string]*string - Iface interface{} - Ifaceptr interface{} -} - -func (s *S) PointerMethod() string { return "ptrmethod!" } - -func (s S) ValueMethod() string { return "valmethod!" } - -var t1 = T{"ItemNumber1", "ValueNumber1"} -var t2 = T{"ItemNumber2", "ValueNumber2"} - -func uppercase(v interface{}) string { - s := v.(string) - t := "" - for i := 0; i < len(s); i++ { - c := s[i] - if 'a' <= c && c <= 'z' { - c = c + 'A' - 'a' - } - t += string(c) - } - return t -} - -func plus1(v interface{}) string { - i := v.(int) - return fmt.Sprint(i + 1) -} - -func writer(f func(interface{}) string) func(io.Writer, string, ...interface{}) { - return func(w io.Writer, format string, v ...interface{}) { - if len(v) != 1 { - panic("test writer expected one arg") - } - io.WriteString(w, f(v[0])) - } -} - -func multiword(w io.Writer, format string, value ...interface{}) { - for _, v := range value { - fmt.Fprintf(w, "<%v>", v) - } -} - -func printf(w io.Writer, format string, v ...interface{}) { - io.WriteString(w, fmt.Sprintf(v[0].(string), v[1:]...)) -} - -var formatters = FormatterMap{ - "uppercase": writer(uppercase), - "+1": writer(plus1), - "multiword": multiword, - "printf": printf, -} - -var tests = []*Test{ - // Simple - &Test{"", "", ""}, - &Test{"abc", "abc", ""}, - &Test{"abc\ndef\n", "abc\ndef\n", ""}, - &Test{" {.meta-left} \n", "{", ""}, - &Test{" {.meta-right} \n", "}", ""}, - &Test{" {.space} \n", " ", ""}, - &Test{" {.tab} \n", "\t", ""}, - &Test{" {#comment} \n", "", ""}, - &Test{"\tSome Text\t\n", "\tSome Text\t\n", ""}, - &Test{" {.meta-right} {.meta-right} {.meta-right} \n", " } } } \n", ""}, - - // Variables at top level - &Test{ - in: "{Header}={Integer}\n", - - out: "Header=77\n", - }, - - &Test{ - in: "Pointers: {*HeaderPtr}={*IntegerPtr}\n", - - out: "Pointers: Header=77\n", - }, - - &Test{ - in: "Stars but not pointers: {*Header}={*Integer}\n", - - out: "Stars but not pointers: Header=77\n", - }, - - &Test{ - in: "nil pointer: {*NilPtr}={*Integer}\n", - - out: "nil pointer: =77\n", - }, - - &Test{ - in: `{"Strings" ":"} {""} {"|"} {"\t\u0123 \x23\\"} {"\"}{\\"}`, - - out: "Strings: | \t\u0123 \x23\\ \"}{\\", - }, - - &Test{ - in: "{`Raw strings` `:`} {``} {`|`} {`\\t\\u0123 \\x23\\`} {`}{\\`}", - - out: "Raw strings: | \\t\\u0123 \\x23\\ }{\\", - }, - - &Test{ - in: "Characters: {'a'} {'\\u0123'} {' '} {'{'} {'|'} {'}'}", - - out: "Characters: 97 291 32 123 124 125", - }, - - &Test{ - in: "Integers: {1} {-2} {+42} {0777} {0x0a}", - - out: "Integers: 1 -2 42 511 10", - }, - - &Test{ - in: "Floats: {.5} {-.5} {1.1} {-2.2} {+42.1} {1e10} {1.2e-3} {1.2e3} {-1.2e3}", - - out: "Floats: 0.5 -0.5 1.1 -2.2 42.1 1e+10 0.0012 1200 -1200", - }, - - // Method at top level - &Test{ - in: "ptrmethod={PointerMethod}\n", - - out: "ptrmethod=ptrmethod!\n", - }, - - &Test{ - in: "valmethod={ValueMethod}\n", - - out: "valmethod=valmethod!\n", - }, - - // Section - &Test{ - in: "{.section Data }\n" + - "some text for the section\n" + - "{.end}\n", - - out: "some text for the section\n", - }, - &Test{ - in: "{.section Data }\n" + - "{Header}={Integer}\n" + - "{.end}\n", - - out: "Header=77\n", - }, - &Test{ - in: "{.section Pdata }\n" + - "{Header}={Integer}\n" + - "{.end}\n", - - out: "Header=77\n", - }, - &Test{ - in: "{.section Pdata }\n" + - "data present\n" + - "{.or}\n" + - "data not present\n" + - "{.end}\n", - - out: "data present\n", - }, - &Test{ - in: "{.section Empty }\n" + - "data present\n" + - "{.or}\n" + - "data not present\n" + - "{.end}\n", - - out: "data not present\n", - }, - &Test{ - in: "{.section Null }\n" + - "data present\n" + - "{.or}\n" + - "data not present\n" + - "{.end}\n", - - out: "data not present\n", - }, - &Test{ - in: "{.section Pdata }\n" + - "{Header}={Integer}\n" + - "{.section @ }\n" + - "{Header}={Integer}\n" + - "{.end}\n" + - "{.end}\n", - - out: "Header=77\n" + - "Header=77\n", - }, - - &Test{ - in: "{.section Data}{.end} {Header}\n", - - out: " Header\n", - }, - - &Test{ - in: "{.section Integer}{@}{.end}", - - out: "77", - }, - - // Repeated - &Test{ - in: "{.section Pdata }\n" + - "{.repeated section @ }\n" + - "{Item}={Value}\n" + - "{.end}\n" + - "{.end}\n", - - out: "ItemNumber1=ValueNumber1\n" + - "ItemNumber2=ValueNumber2\n", - }, - &Test{ - in: "{.section Pdata }\n" + - "{.repeated section @ }\n" + - "{Item}={Value}\n" + - "{.or}\n" + - "this should not appear\n" + - "{.end}\n" + - "{.end}\n", - - out: "ItemNumber1=ValueNumber1\n" + - "ItemNumber2=ValueNumber2\n", - }, - &Test{ - in: "{.section @ }\n" + - "{.repeated section Empty }\n" + - "{Item}={Value}\n" + - "{.or}\n" + - "this should appear: empty field\n" + - "{.end}\n" + - "{.end}\n", - - out: "this should appear: empty field\n", - }, - &Test{ - in: "{.repeated section Pdata }\n" + - "{Item}\n" + - "{.alternates with}\n" + - "is\nover\nmultiple\nlines\n" + - "{.end}\n", - - out: "ItemNumber1\n" + - "is\nover\nmultiple\nlines\n" + - "ItemNumber2\n", - }, - &Test{ - in: "{.repeated section Pdata }\n" + - "{Item}\n" + - "{.alternates with}\n" + - "is\nover\nmultiple\nlines\n" + - " {.end}\n", - - out: "ItemNumber1\n" + - "is\nover\nmultiple\nlines\n" + - "ItemNumber2\n", - }, - &Test{ - in: "{.section Pdata }\n" + - "{.repeated section @ }\n" + - "{Item}={Value}\n" + - "{.alternates with}DIVIDER\n" + - "{.or}\n" + - "this should not appear\n" + - "{.end}\n" + - "{.end}\n", - - out: "ItemNumber1=ValueNumber1\n" + - "DIVIDER\n" + - "ItemNumber2=ValueNumber2\n", - }, - &Test{ - in: "{.repeated section Vec }\n" + - "{@}\n" + - "{.end}\n", - - out: "elt1\n" + - "elt2\n", - }, - // Same but with a space before {.end}: was a bug. - &Test{ - in: "{.repeated section Vec }\n" + - "{@} {.end}\n", - - out: "elt1 elt2 \n", - }, - &Test{ - in: "{.repeated section Integer}{.end}", - - err: "line 1: .repeated: cannot repeat Integer (type int)", - }, - - // Nested names - &Test{ - in: "{.section @ }\n" + - "{InnerT.Item}={InnerT.Value}\n" + - "{.end}", - - out: "ItemNumber1=ValueNumber1\n", - }, - &Test{ - in: "{.section @ }\n" + - "{InnerT.Item}={.section InnerT}{.section Value}{@}{.end}{.end}\n" + - "{.end}", - - out: "ItemNumber1=ValueNumber1\n", - }, - - &Test{ - in: "{.section Emptystring}emptystring{.end}\n" + - "{.section Header}header{.end}\n", - - out: "\nheader\n", - }, - - &Test{ - in: "{.section True}1{.or}2{.end}\n" + - "{.section False}3{.or}4{.end}\n", - - out: "1\n4\n", - }, - - // Maps - - &Test{ - in: "{Mp.mapkey}\n", - - out: "Ahoy!\n", - }, - &Test{ - in: "{Innermap.Mp.innerkey}\n", - - out: "55\n", - }, - &Test{ - in: "{.section Innermap}{.section Mp}{innerkey}{.end}{.end}\n", - - out: "55\n", - }, - &Test{ - in: "{.section JSON}{.repeated section maps}{a}{b}{.end}{.end}\n", - - out: "1234\n", - }, - &Test{ - in: "{Stringmap.stringkey1}\n", - - out: "stringresult\n", - }, - &Test{ - in: "{.repeated section Stringmap}\n" + - "{@}\n" + - "{.end}", - - out: "stringresult\n" + - "stringresult\n", - }, - &Test{ - in: "{.repeated section Stringmap}\n" + - "\t{@}\n" + - "{.end}", - - out: "\tstringresult\n" + - "\tstringresult\n", - }, - &Test{ - in: "{*Ptrmap.stringkey1}\n", - - out: "pointedToString\n", - }, - &Test{ - in: "{.repeated section Ptrmap}\n" + - "{*@}\n" + - "{.end}", - - out: "pointedToString\n" + - "pointedToString\n", - }, - - // Interface values - - &Test{ - in: "{Iface}", - - out: "[1 2 3]", - }, - &Test{ - in: "{.repeated section Iface}{@}{.alternates with} {.end}", - - out: "1 2 3", - }, - &Test{ - in: "{.section Iface}{@}{.end}", - - out: "[1 2 3]", - }, - &Test{ - in: "{.section Ifaceptr}{Item} {Value}{.end}", - - out: "Item Value", - }, -} - -func TestAll(t *testing.T) { - // Parse - testAll(t, func(test *Test) (*Template, os.Error) { return Parse(test.in, formatters) }) - // ParseFile - testAll(t, func(test *Test) (*Template, os.Error) { - err := ioutil.WriteFile("_test/test.tmpl", []byte(test.in), 0600) - if err != nil { - t.Error("unexpected write error:", err) - return nil, err - } - return ParseFile("_test/test.tmpl", formatters) - }) - // tmpl.ParseFile - testAll(t, func(test *Test) (*Template, os.Error) { - err := ioutil.WriteFile("_test/test.tmpl", []byte(test.in), 0600) - if err != nil { - t.Error("unexpected write error:", err) - return nil, err - } - tmpl := New(formatters) - return tmpl, tmpl.ParseFile("_test/test.tmpl") - }) -} - -func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) { - s := new(S) - // initialized by hand for clarity. - s.Header = "Header" - s.HeaderPtr = &s.Header - s.Integer = 77 - s.IntegerPtr = &s.Integer - s.InnerT = t1 - s.Data = []T{t1, t2} - s.Pdata = []*T{&t1, &t2} - s.Empty = []*T{} - s.Null = nil - s.Vec = new(vector.Vector) - s.Vec.Push("elt1") - s.Vec.Push("elt2") - s.True = true - s.False = false - s.Mp = make(map[string]string) - s.Mp["mapkey"] = "Ahoy!" - json.Unmarshal([]byte(`{"maps":[{"a":1,"b":2},{"a":3,"b":4}]}`), &s.JSON) - s.Innermap.Mp = make(map[string]int) - s.Innermap.Mp["innerkey"] = 55 - s.Stringmap = make(map[string]string) - s.Stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent - s.Stringmap["stringkey2"] = "stringresult" - s.Ptrmap = make(map[string]*string) - x := "pointedToString" - s.Ptrmap["stringkey1"] = &x // the same value so repeated section is order-independent - s.Ptrmap["stringkey2"] = &x - s.Iface = []int{1, 2, 3} - s.Ifaceptr = &T{"Item", "Value"} - - var buf bytes.Buffer - for _, test := range tests { - buf.Reset() - tmpl, err := parseFunc(test) - if err != nil { - t.Error("unexpected parse error: ", err) - continue - } - err = tmpl.Execute(&buf, s) - if test.err == "" { - if err != nil { - t.Error("unexpected execute error:", err) - } - } else { - if err == nil { - t.Errorf("expected execute error %q, got nil", test.err) - } else if err.String() != test.err { - t.Errorf("expected execute error %q, got %q", test.err, err.String()) - } - } - if buf.String() != test.out { - t.Errorf("for %q: expected %q got %q", test.in, test.out, buf.String()) - } - } -} - -func TestMapDriverType(t *testing.T) { - mp := map[string]string{"footer": "Ahoy!"} - tmpl, err := Parse("template: {footer}", nil) - if err != nil { - t.Error("unexpected parse error:", err) - } - var b bytes.Buffer - err = tmpl.Execute(&b, mp) - if err != nil { - t.Error("unexpected execute error:", err) - } - s := b.String() - expect := "template: Ahoy!" - if s != expect { - t.Errorf("failed passing string as data: expected %q got %q", expect, s) - } -} - -func TestMapNoEntry(t *testing.T) { - mp := make(map[string]int) - tmpl, err := Parse("template: {notthere}!", nil) - if err != nil { - t.Error("unexpected parse error:", err) - } - var b bytes.Buffer - err = tmpl.Execute(&b, mp) - if err != nil { - t.Error("unexpected execute error:", err) - } - s := b.String() - expect := "template: 0!" - if s != expect { - t.Errorf("failed passing string as data: expected %q got %q", expect, s) - } -} - -func TestStringDriverType(t *testing.T) { - tmpl, err := Parse("template: {@}", nil) - if err != nil { - t.Error("unexpected parse error:", err) - } - var b bytes.Buffer - err = tmpl.Execute(&b, "hello") - if err != nil { - t.Error("unexpected execute error:", err) - } - s := b.String() - expect := "template: hello" - if s != expect { - t.Errorf("failed passing string as data: expected %q got %q", expect, s) - } -} - -func TestTwice(t *testing.T) { - tmpl, err := Parse("template: {@}", nil) - if err != nil { - t.Error("unexpected parse error:", err) - } - var b bytes.Buffer - err = tmpl.Execute(&b, "hello") - if err != nil { - t.Error("unexpected parse error:", err) - } - s := b.String() - expect := "template: hello" - if s != expect { - t.Errorf("failed passing string as data: expected %q got %q", expect, s) - } - err = tmpl.Execute(&b, "hello") - if err != nil { - t.Error("unexpected parse error:", err) - } - s = b.String() - expect += expect - if s != expect { - t.Errorf("failed passing string as data: expected %q got %q", expect, s) - } -} - -func TestCustomDelims(t *testing.T) { - // try various lengths. zero should catch error. - for i := 0; i < 7; i++ { - for j := 0; j < 7; j++ { - tmpl := New(nil) - // first two chars deliberately the same to test equal left and right delims - ldelim := "$!#$%^&"[0:i] - rdelim := "$*&^%$!"[0:j] - tmpl.SetDelims(ldelim, rdelim) - // if braces, this would be template: {@}{.meta-left}{.meta-right} - text := "template: " + - ldelim + "@" + rdelim + - ldelim + ".meta-left" + rdelim + - ldelim + ".meta-right" + rdelim - err := tmpl.Parse(text) - if err != nil { - if i == 0 || j == 0 { // expected - continue - } - t.Error("unexpected parse error:", err) - } else if i == 0 || j == 0 { - t.Errorf("expected parse error for empty delimiter: %d %d %q %q", i, j, ldelim, rdelim) - continue - } - var b bytes.Buffer - err = tmpl.Execute(&b, "hello") - s := b.String() - if s != "template: hello"+ldelim+rdelim { - t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s) - } - } - } -} - -// Test that a variable evaluates to the field itself and does not further indirection -func TestVarIndirection(t *testing.T) { - s := new(S) - // initialized by hand for clarity. - s.InnerPointerT = &t1 - - var buf bytes.Buffer - input := "{.section @}{InnerPointerT}{.end}" - tmpl, err := Parse(input, nil) - if err != nil { - t.Fatal("unexpected parse error:", err) - } - err = tmpl.Execute(&buf, s) - if err != nil { - t.Fatal("unexpected execute error:", err) - } - expect := fmt.Sprintf("%v", &t1) // output should be hex address of t1 - if buf.String() != expect { - t.Errorf("for %q: expected %q got %q", input, expect, buf.String()) - } -} - -func TestHTMLFormatterWithByte(t *testing.T) { - s := "Test string." - b := []byte(s) - var buf bytes.Buffer - HTMLFormatter(&buf, "", b) - bs := buf.String() - if bs != s { - t.Errorf("munged []byte, expected: %s got: %s", s, bs) - } -} - -type UF struct { - I int - s string -} - -func TestReferenceToUnexported(t *testing.T) { - u := &UF{3, "hello"} - var buf bytes.Buffer - input := "{.section @}{I}{s}{.end}" - tmpl, err := Parse(input, nil) - if err != nil { - t.Fatal("unexpected parse error:", err) - } - err = tmpl.Execute(&buf, u) - if err == nil { - t.Fatal("expected execute error, got none") - } - if strings.Index(err.String(), "not exported") < 0 { - t.Fatal("expected unexported error; got", err) - } -} - -var formatterTests = []Test{ - { - in: "{Header|uppercase}={Integer|+1}\n" + - "{Header|html}={Integer|str}\n", - - out: "HEADER=78\n" + - "Header=77\n", - }, - - { - in: "{Header|uppercase}={Integer Header|multiword}\n" + - "{Header|html}={Header Integer|multiword}\n" + - "{Header|html}={Header Integer}\n", - - out: "HEADER=<77>
\n" + - "Header=
<77>\n" + - "Header=Header77\n", - }, - { - in: "{Raw}\n" + - "{Raw|html}\n", - - out: "a <&> b\n" + - "a <&> b\n", - }, - { - in: "{Bytes}", - out: "hello", - }, - { - in: "{Raw|uppercase|html|html}", - out: "A &lt;&amp;&gt; B", - }, - { - in: "{Header Integer|multiword|html}", - out: "<Header><77>", - }, - { - in: "{Integer|no_formatter|html}", - err: `unknown formatter: "no_formatter"`, - }, - { - in: "{Integer|||||}", // empty string is a valid formatter - out: "77", - }, - { - in: `{"%.02f 0x%02X" 1.1 10|printf}`, - out: "1.10 0x0A", - }, - { - in: `{""|}{""||}{""|printf}`, // Issue #1896. - out: "", - }, -} - -func TestFormatters(t *testing.T) { - data := map[string]interface{}{ - "Header": "Header", - "Integer": 77, - "Raw": "a <&> b", - "Bytes": []byte("hello"), - } - for _, c := range formatterTests { - tmpl, err := Parse(c.in, formatters) - if err != nil { - if c.err == "" { - t.Error("unexpected parse error:", err) - continue - } - if strings.Index(err.String(), c.err) < 0 { - t.Errorf("unexpected error: expected %q, got %q", c.err, err.String()) - continue - } - } else { - if c.err != "" { - t.Errorf("For %q, expected error, got none.", c.in) - continue - } - buf := bytes.NewBuffer(nil) - err = tmpl.Execute(buf, data) - if err != nil { - t.Error("unexpected Execute error: ", err) - continue - } - actual := buf.String() - if actual != c.out { - t.Errorf("for %q: expected %q but got %q.", c.in, c.out, actual) - } - } - } -} diff --git a/src/pkg/template/testdata/file1.tmpl b/src/pkg/template/testdata/file1.tmpl new file mode 100644 index 000000000..febf9d9f8 --- /dev/null +++ b/src/pkg/template/testdata/file1.tmpl @@ -0,0 +1,2 @@ +{{define "x"}}TEXT{{end}} +{{define "dotV"}}{{.V}}{{end}} diff --git a/src/pkg/template/testdata/file2.tmpl b/src/pkg/template/testdata/file2.tmpl new file mode 100644 index 000000000..39bf6fb9e --- /dev/null +++ b/src/pkg/template/testdata/file2.tmpl @@ -0,0 +1,2 @@ +{{define "dot"}}{{.}}{{end}} +{{define "nested"}}{{template "dot" .}}{{end}} diff --git a/src/pkg/template/testdata/tmpl1.tmpl b/src/pkg/template/testdata/tmpl1.tmpl new file mode 100644 index 000000000..3d15b8173 --- /dev/null +++ b/src/pkg/template/testdata/tmpl1.tmpl @@ -0,0 +1 @@ +template1 diff --git a/src/pkg/template/testdata/tmpl2.tmpl b/src/pkg/template/testdata/tmpl2.tmpl new file mode 100644 index 000000000..a374d2fe7 --- /dev/null +++ b/src/pkg/template/testdata/tmpl2.tmpl @@ -0,0 +1 @@ +template2 diff --git a/src/pkg/time/Makefile b/src/pkg/time/Makefile index 023e8775e..a6fce3fa1 100644 --- a/src/pkg/time/Makefile +++ b/src/pkg/time/Makefile @@ -27,6 +27,11 @@ GOFILES_linux=\ zoneinfo_posix.go\ zoneinfo_unix.go\ +GOFILES_openbsd=\ + sys_posix.go\ + zoneinfo_posix.go\ + zoneinfo_unix.go\ + GOFILES_windows=\ sys_posix.go\ zoneinfo_windows.go\ diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go index 3c42f0c2d..5ddd54812 100644 --- a/src/pkg/time/format.go +++ b/src/pkg/time/format.go @@ -26,8 +26,11 @@ const ( // replaced by a digit if the following number (a day) has two digits; for // compatibility with fixed-width Unix time formats. // -// A decimal point followed by one or more zeros represents a -// fractional second. +// A decimal point followed by one or more zeros represents a fractional +// second. When parsing (only), the input may contain a fractional second +// field immediately after the seconds field, even if the layout does not +// signify its presence. In that case a decimal point followed by a maximal +// series of digits is parsed as a fractional second. // // Numeric time zone offsets format as follows: // -0700 ±hhmm @@ -169,7 +172,7 @@ func nextStdChunk(layout string) (prefix, std, suffix string) { numZeros++ } // String of digits must end here - only fractional second is all zeros. - if numZeros > 0 && (j >= len(layout) || layout[j] < '0' || '9' < layout[j]) { + if numZeros > 0 && !isDigit(layout, j) { return layout[0:i], layout[i : i+1+numZeros], layout[i+1+numZeros:] } } @@ -416,14 +419,24 @@ func (e *ParseError) String() string { strconv.Quote(e.Value) + e.Message } +// isDigit returns true if s[i] is a decimal digit, false if not or +// if s[i] is out of range. +func isDigit(s string, i int) bool { + if len(s) <= i { + return false + } + c := s[i] + return '0' <= c && c <= '9' +} + // getnum parses s[0:1] or s[0:2] (fixed forces the latter) // as a decimal integer and returns the integer and the // remainder of the string. func getnum(s string, fixed bool) (int, string, os.Error) { - if len(s) == 0 || s[0] < '0' || s[0] > '9' { + if !isDigit(s, 0) { return 0, s, errBad } - if len(s) == 1 || s[1] < '0' || s[1] > '9' { + if !isDigit(s, 1) { if fixed { return 0, s, errBad } @@ -509,7 +522,7 @@ func Parse(alayout, avalue string) (*Time, os.Error) { t.Year += 2000 } case stdLongYear: - if len(value) < 4 || value[0] < '0' || value[0] > '9' { + if len(value) < 4 || !isDigit(value, 0) { err = errBad break } @@ -557,6 +570,21 @@ func Parse(alayout, avalue string) (*Time, os.Error) { if t.Second < 0 || 60 <= t.Second { rangeErrString = "second" } + // Special case: do we have a fractional second but no + // fractional second in the format? + if len(value) > 2 && value[0] == '.' && isDigit(value, 1) { + _, std, _ := nextStdChunk(layout) + if len(std) > 0 && std[0] == '.' && isDigit(std, 1) { + // Fractional second in the layout; proceed normally + break + } + // No fractional second in the layout but we have one in the input. + n := 2 + for ; n < len(value) && isDigit(value, n); n++ { + } + rangeErrString, err = t.parseNanoseconds(value, n) + value = value[n:] + } case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ: if std[0] == 'Z' && len(value) >= 1 && value[0] == 'Z' { value = value[1:] @@ -663,26 +691,8 @@ func Parse(alayout, avalue string) (*Time, os.Error) { break } if len(std) >= 2 && std[0:2] == ".0" { - if value[0] != '.' { - err = errBad - break - } - t.Nanosecond, err = strconv.Atoi(value[1:len(std)]) - if err != nil { - break - } - if t.Nanosecond < 0 || t.Nanosecond >= 1e9 { - rangeErrString = "fractional second" - break - } + rangeErrString, err = t.parseNanoseconds(value, len(std)) value = value[len(std):] - // We need nanoseconds, which means scaling by the number - // of missing digits in the format, maximum length 10. If it's - // longer than 10, we won't scale. - scaleDigits := 10 - len(std) - for i := 0; i < scaleDigits; i++ { - t.Nanosecond *= 10 - } } } if rangeErrString != "" { @@ -699,3 +709,26 @@ func Parse(alayout, avalue string) (*Time, os.Error) { } return &t, nil } + +func (t *Time) parseNanoseconds(value string, nbytes int) (rangErrString string, err os.Error) { + if value[0] != '.' { + return "", errBad + } + var ns int + ns, err = strconv.Atoi(value[1:nbytes]) + if err != nil { + return "", err + } + if ns < 0 || 1e9 <= ns { + return "fractional second", nil + } + // We need nanoseconds, which means scaling by the number + // of missing digits in the format, maximum length 10. If it's + // longer than 10, we won't scale. + scaleDigits := 10 - nbytes + for i := 0; i < scaleDigits; i++ { + ns *= 10 + } + t.Nanosecond = ns + return +} diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go index 4999f4536..dceed491a 100644 --- a/src/pkg/time/time_test.go +++ b/src/pkg/time/time_test.go @@ -250,6 +250,13 @@ var parseTests = []ParseTest{ {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true, 1, 0}, {"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00", true, false, 1, 0}, {"custom: \"2006-01-02 15:04:05-07\"", "2006-01-02 15:04:05-07", "2010-02-04 21:00:57-08", true, false, 1, 0}, + // Optional fractional seconds. + {"ANSIC", ANSIC, "Thu Feb 4 21:00:57.0 2010", false, true, 1, 1}, + {"UnixDate", UnixDate, "Thu Feb 4 21:00:57.01 PST 2010", true, true, 1, 2}, + {"RubyDate", RubyDate, "Thu Feb 04 21:00:57.012 -0800 2010", true, true, 1, 3}, + {"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57.0123 PST", true, true, 1, 4}, + {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57.01234 PST", true, true, 1, 5}, + {"RFC3339", RFC3339, "2010-02-04T21:00:57.012345678-08:00", true, false, 1, 9}, // Amount of white space should not matter. {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0}, {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0}, diff --git a/src/pkg/url/Makefile b/src/pkg/url/Makefile new file mode 100644 index 000000000..b9267bd08 --- /dev/null +++ b/src/pkg/url/Makefile @@ -0,0 +1,11 @@ +# 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. + +include ../../Make.inc + +TARG=url +GOFILES=\ + url.go\ + +include ../../Make.pkg diff --git a/src/pkg/url/url.go b/src/pkg/url/url.go new file mode 100644 index 000000000..d07b01611 --- /dev/null +++ b/src/pkg/url/url.go @@ -0,0 +1,677 @@ +// 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 URL parses URLs and implements query escaping. +// See RFC 3986. +package url + +import ( + "os" + "strconv" + "strings" +) + +// Error reports an error and the operation and URL that caused it. +type Error struct { + Op string + URL string + Error os.Error +} + +func (e *Error) String() string { return e.Op + " " + e.URL + ": " + e.Error.String() } + +func ishex(c byte) bool { + switch { + case '0' <= c && c <= '9': + return true + case 'a' <= c && c <= 'f': + return true + case 'A' <= c && c <= 'F': + return true + } + return false +} + +func unhex(c byte) byte { + switch { + case '0' <= c && c <= '9': + return c - '0' + case 'a' <= c && c <= 'f': + return c - 'a' + 10 + case 'A' <= c && c <= 'F': + return c - 'A' + 10 + } + return 0 +} + +type encoding int + +const ( + encodePath encoding = 1 + iota + encodeUserPassword + encodeQueryComponent + encodeFragment + encodeOpaque +) + +type EscapeError string + +func (e EscapeError) String() string { + return "invalid URL escape " + strconv.Quote(string(e)) +} + +// Return true if the specified character should be escaped when +// appearing in a URL string, according to RFC 2396. +// When 'all' is true the full range of reserved characters are matched. +func shouldEscape(c byte, mode encoding) bool { + // RFC 2396 §2.3 Unreserved characters (alphanum) + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { + return false + } + switch c { + case '-', '_', '.', '!', '~', '*', '\'', '(', ')': // §2.3 Unreserved characters (mark) + return false + + case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved) + // Different sections of the URL allow a few of + // the reserved characters to appear unescaped. + switch mode { + case encodePath: // §3.3 + // The RFC allows : @ & = + $ , but saves / ; for assigning + // meaning to individual path segments. This package + // only manipulates the path as a whole, so we allow those + // last two as well. Clients that need to distinguish between + // `/foo;y=z/bar` and `/foo%3by=z/bar` will have to re-decode RawPath. + // That leaves only ? to escape. + return c == '?' + + case encodeUserPassword: // §3.2.2 + // The RFC allows ; : & = + $ , in userinfo, so we must escape only @ and /. + // The parsing of userinfo treats : as special so we must escape that too. + return c == '@' || c == '/' || c == ':' + + case encodeQueryComponent: // §3.4 + // The RFC reserves (so we must escape) everything. + return true + + case encodeFragment: // §4.1 + // The RFC text is silent but the grammar allows + // everything, so escape nothing. + return false + + case encodeOpaque: // §3 opaque_part + // The RFC allows opaque_part to use all characters + // except that the leading / must be escaped. + // (We implement that case in String.) + return false + } + } + + // Everything else must be escaped. + return true +} + +// QueryUnescape does the inverse transformation of QueryEscape, converting +// %AB into the byte 0xAB and '+' into ' ' (space). It returns an error if +// any % is not followed by two hexadecimal digits. +func QueryUnescape(s string) (string, os.Error) { + return unescape(s, encodeQueryComponent) +} + +// unescape unescapes a string; the mode specifies +// which section of the URL string is being unescaped. +func unescape(s string, mode encoding) (string, os.Error) { + // Count %, check that they're well-formed. + n := 0 + hasPlus := false + for i := 0; i < len(s); { + switch s[i] { + case '%': + n++ + if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { + s = s[i:] + if len(s) > 3 { + s = s[0:3] + } + return "", EscapeError(s) + } + i += 3 + case '+': + hasPlus = mode == encodeQueryComponent + i++ + default: + i++ + } + } + + if n == 0 && !hasPlus { + return s, nil + } + + t := make([]byte, len(s)-2*n) + j := 0 + for i := 0; i < len(s); { + switch s[i] { + case '%': + t[j] = unhex(s[i+1])<<4 | unhex(s[i+2]) + j++ + i += 3 + case '+': + if mode == encodeQueryComponent { + t[j] = ' ' + } else { + t[j] = '+' + } + j++ + i++ + default: + t[j] = s[i] + j++ + i++ + } + } + return string(t), nil +} + +// QueryEscape escapes the string so it can be safely placed +// inside a URL query. +func QueryEscape(s string) string { + return escape(s, encodeQueryComponent) +} + +func escape(s string, mode encoding) string { + spaceCount, hexCount := 0, 0 + for i := 0; i < len(s); i++ { + c := s[i] + if shouldEscape(c, mode) { + if c == ' ' && mode == encodeQueryComponent { + spaceCount++ + } else { + hexCount++ + } + } + } + + if spaceCount == 0 && hexCount == 0 { + return s + } + + t := make([]byte, len(s)+2*hexCount) + j := 0 + for i := 0; i < len(s); i++ { + switch c := s[i]; { + case c == ' ' && mode == encodeQueryComponent: + t[j] = '+' + j++ + case shouldEscape(c, mode): + t[j] = '%' + t[j+1] = "0123456789ABCDEF"[c>>4] + t[j+2] = "0123456789ABCDEF"[c&15] + j += 3 + default: + t[j] = s[i] + j++ + } + } + return string(t) +} + +// UnescapeUserinfo parses the RawUserinfo field of a URL +// as the form user or user:password and unescapes and returns +// the two halves. +// +// This functionality should only be used with legacy web sites. +// RFC 2396 warns that interpreting Userinfo this way +// ``is NOT RECOMMENDED, because the passing of authentication +// information in clear text (such as URI) has proven to be a +// security risk in almost every case where it has been used.'' +func UnescapeUserinfo(rawUserinfo string) (user, password string, err os.Error) { + u, p := split(rawUserinfo, ':', true) + if user, err = unescape(u, encodeUserPassword); err != nil { + return "", "", err + } + if password, err = unescape(p, encodeUserPassword); err != nil { + return "", "", err + } + return +} + +// EscapeUserinfo combines user and password in the form +// user:password (or just user if password is empty) and then +// escapes it for use as the URL.RawUserinfo field. +// +// This functionality should only be used with legacy web sites. +// RFC 2396 warns that interpreting Userinfo this way +// ``is NOT RECOMMENDED, because the passing of authentication +// information in clear text (such as URI) has proven to be a +// security risk in almost every case where it has been used.'' +func EscapeUserinfo(user, password string) string { + raw := escape(user, encodeUserPassword) + if password != "" { + raw += ":" + escape(password, encodeUserPassword) + } + return raw +} + +// A URL represents a parsed URL (technically, a URI reference). +// The general form represented is: +// scheme://[userinfo@]host/path[?query][#fragment] +// The Raw, RawAuthority, RawPath, and RawQuery fields are in "wire format" +// (special characters must be hex-escaped if not meant to have special meaning). +// All other fields are logical values; '+' or '%' represent themselves. +// +// The various Raw values are supplied in wire format because +// clients typically have to split them into pieces before further +// decoding. +type URL struct { + Raw string // the original string + Scheme string // scheme + RawAuthority string // [userinfo@]host + RawUserinfo string // userinfo + Host string // host + RawPath string // /path[?query][#fragment] + Path string // /path + OpaquePath bool // path is opaque (unrooted when scheme is present) + RawQuery string // query + Fragment string // fragment +} + +// Maybe rawurl is of the form scheme:path. +// (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*) +// If so, return scheme, path; else return "", rawurl. +func getscheme(rawurl string) (scheme, path string, err os.Error) { + for i := 0; i < len(rawurl); i++ { + c := rawurl[i] + switch { + case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': + // do nothing + case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.': + if i == 0 { + return "", rawurl, nil + } + case c == ':': + if i == 0 { + return "", "", os.NewError("missing protocol scheme") + } + return rawurl[0:i], rawurl[i+1:], nil + default: + // we have encountered an invalid character, + // so there is no valid scheme + return "", rawurl, nil + } + } + return "", rawurl, nil +} + +// Maybe s is of the form t c u. +// If so, return t, c u (or t, u if cutc == true). +// If not, return s, "". +func split(s string, c byte, cutc bool) (string, string) { + for i := 0; i < len(s); i++ { + if s[i] == c { + if cutc { + return s[0:i], s[i+1:] + } + return s[0:i], s[i:] + } + } + return s, "" +} + +// Parse parses rawurl into a URL structure. +// The string rawurl is assumed not to have a #fragment suffix. +// (Web browsers strip #fragment before sending the URL to a web server.) +// The rawurl may be relative or absolute. +func Parse(rawurl string) (url *URL, err os.Error) { + return parse(rawurl, false) +} + +// ParseRequest parses rawurl into a URL structure. It assumes that +// rawurl was received from an HTTP request, so the rawurl is interpreted +// only as an absolute URI or an absolute path. +// The string rawurl is assumed not to have a #fragment suffix. +// (Web browsers strip #fragment before sending the URL to a web server.) +func ParseRequest(rawurl string) (url *URL, err os.Error) { + return parse(rawurl, true) +} + +// parse parses a URL from a string in one of two contexts. If +// viaRequest is true, the URL is assumed to have arrived via an HTTP request, +// in which case only absolute URLs or path-absolute relative URLs are allowed. +// If viaRequest is false, all forms of relative URLs are allowed. +func parse(rawurl string, viaRequest bool) (url *URL, err os.Error) { + var ( + leadingSlash bool + path string + ) + + if rawurl == "" { + err = os.NewError("empty url") + goto Error + } + url = new(URL) + url.Raw = rawurl + + // Split off possible leading "http:", "mailto:", etc. + // Cannot contain escaped characters. + if url.Scheme, path, err = getscheme(rawurl); err != nil { + goto Error + } + leadingSlash = strings.HasPrefix(path, "/") + + if url.Scheme != "" && !leadingSlash { + // RFC 2396: + // Absolute URI (has scheme) with non-rooted path + // is uninterpreted. It doesn't even have a ?query. + // This is the case that handles mailto:name@example.com. + url.RawPath = path + + if url.Path, err = unescape(path, encodeOpaque); err != nil { + goto Error + } + url.OpaquePath = true + } else { + if viaRequest && !leadingSlash { + err = os.NewError("invalid URI for request") + goto Error + } + + // Split off query before parsing path further. + url.RawPath = path + path, query := split(path, '?', false) + if len(query) > 1 { + url.RawQuery = query[1:] + } + + // Maybe path is //authority/path + if (url.Scheme != "" || !viaRequest) && + strings.HasPrefix(path, "//") && !strings.HasPrefix(path, "///") { + url.RawAuthority, path = split(path[2:], '/', false) + url.RawPath = url.RawPath[2+len(url.RawAuthority):] + } + + // Split authority into userinfo@host. + // If there's no @, split's default is wrong. Check explicitly. + var rawHost string + if strings.Index(url.RawAuthority, "@") < 0 { + rawHost = url.RawAuthority + } else { + url.RawUserinfo, rawHost = split(url.RawAuthority, '@', true) + } + + // We leave RawAuthority only in raw form because clients + // of common protocols should be using Userinfo and Host + // instead. Clients that wish to use RawAuthority will have to + // interpret it themselves: RFC 2396 does not define the meaning. + + if strings.Contains(rawHost, "%") { + // Host cannot contain escaped characters. + err = os.NewError("hexadecimal escape in host") + goto Error + } + url.Host = rawHost + + if url.Path, err = unescape(path, encodePath); err != nil { + goto Error + } + } + return url, nil + +Error: + return nil, &Error{"parse", rawurl, err} + +} + +// ParseWithReference is like Parse but allows a trailing #fragment. +func ParseWithReference(rawurlref string) (url *URL, err os.Error) { + // Cut off #frag. + rawurl, frag := split(rawurlref, '#', false) + if url, err = Parse(rawurl); err != nil { + return nil, err + } + url.Raw += frag + url.RawPath += frag + if len(frag) > 1 { + frag = frag[1:] + if url.Fragment, err = unescape(frag, encodeFragment); err != nil { + return nil, &Error{"parse", rawurl, err} + } + } + return url, nil +} + +// String reassembles url into a valid URL string. +// +// There are redundant fields stored in the URL structure: +// the String method consults Scheme, Path, Host, RawUserinfo, +// RawQuery, and Fragment, but not Raw, RawPath or RawAuthority. +func (url *URL) String() string { + result := "" + if url.Scheme != "" { + result += url.Scheme + ":" + } + if url.Host != "" || url.RawUserinfo != "" { + result += "//" + if url.RawUserinfo != "" { + // hide the password, if any + info := url.RawUserinfo + if i := strings.Index(info, ":"); i >= 0 { + info = info[0:i] + ":******" + } + result += info + "@" + } + result += url.Host + } + if url.OpaquePath { + path := url.Path + if strings.HasPrefix(path, "/") { + result += "%2f" + path = path[1:] + } + result += escape(path, encodeOpaque) + } else { + result += escape(url.Path, encodePath) + } + if url.RawQuery != "" { + result += "?" + url.RawQuery + } + if url.Fragment != "" { + result += "#" + escape(url.Fragment, encodeFragment) + } + return result +} + +// Values maps a string key to a list of values. +// It is typically used for query parameters and form values. +// Unlike in the http.Header map, the keys in a Values map +// are case-sensitive. +type Values map[string][]string + +// Get gets the first value associated with the given key. +// If there are no values associated with the key, Get returns +// the empty string. To access multiple values, use the map +// directly. +func (v Values) Get(key string) string { + if v == nil { + return "" + } + vs, ok := v[key] + if !ok || len(vs) == 0 { + return "" + } + return vs[0] +} + +// Set sets the key to value. It replaces any existing +// values. +func (v Values) Set(key, value string) { + v[key] = []string{value} +} + +// Add adds the key to value. It appends to any existing +// values associated with key. +func (v Values) Add(key, value string) { + v[key] = append(v[key], value) +} + +// Del deletes the values associated with key. +func (v Values) Del(key string) { + v[key] = nil, false +} + +// ParseQuery parses the URL-encoded query string and returns +// a map listing the values specified for each key. +// ParseQuery always returns a non-nil map containing all the +// valid query parameters found; err describes the first decoding error +// encountered, if any. +func ParseQuery(query string) (m Values, err os.Error) { + m = make(Values) + err = parseQuery(m, query) + return +} + +func parseQuery(m Values, query string) (err os.Error) { + for _, kv := range strings.Split(query, "&") { + if len(kv) == 0 { + continue + } + kvPair := strings.SplitN(kv, "=", 2) + + var key, value string + var e os.Error + key, e = QueryUnescape(kvPair[0]) + if e == nil && len(kvPair) > 1 { + value, e = QueryUnescape(kvPair[1]) + } + if e != nil { + err = e + continue + } + m[key] = append(m[key], value) + } + return err +} + +// Encode encodes the values into ``URL encoded'' form. +// e.g. "foo=bar&bar=baz" +func (v Values) Encode() string { + if v == nil { + return "" + } + parts := make([]string, 0, len(v)) // will be large enough for most uses + for k, vs := range v { + prefix := QueryEscape(k) + "=" + for _, v := range vs { + parts = append(parts, prefix+QueryEscape(v)) + } + } + return strings.Join(parts, "&") +} + +// resolvePath applies special path segments from refs and applies +// them to base, per RFC 2396. +func resolvePath(basepath string, refpath string) string { + base := strings.Split(basepath, "/") + refs := strings.Split(refpath, "/") + if len(base) == 0 { + base = []string{""} + } + for idx, ref := range refs { + switch { + case ref == ".": + base[len(base)-1] = "" + case ref == "..": + newLen := len(base) - 1 + if newLen < 1 { + newLen = 1 + } + base = base[0:newLen] + base[len(base)-1] = "" + default: + if idx == 0 || base[len(base)-1] == "" { + base[len(base)-1] = ref + } else { + base = append(base, ref) + } + } + } + return strings.Join(base, "/") +} + +// IsAbs returns true if the URL is absolute. +func (url *URL) IsAbs() bool { + return url.Scheme != "" +} + +// Parse parses a URL in the context of a base URL. The URL in ref +// may be relative or absolute. Parse returns nil, err on parse +// failure, otherwise its return value is the same as ResolveReference. +func (base *URL) Parse(ref string) (*URL, os.Error) { + refurl, err := Parse(ref) + if err != nil { + return nil, err + } + return base.ResolveReference(refurl), nil +} + +// ResolveReference resolves a URI reference to an absolute URI from +// an absolute base URI, per RFC 2396 Section 5.2. The URI reference +// may be relative or absolute. ResolveReference always returns a new +// URL instance, even if the returned URL is identical to either the +// base or reference. If ref is an absolute URL, then ResolveReference +// ignores base and returns a copy of ref. +func (base *URL) ResolveReference(ref *URL) *URL { + url := new(URL) + switch { + case ref.IsAbs(): + *url = *ref + default: + // relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] + *url = *base + if ref.RawAuthority != "" { + // The "net_path" case. + url.RawAuthority = ref.RawAuthority + url.Host = ref.Host + url.RawUserinfo = ref.RawUserinfo + } + switch { + case url.OpaquePath: + url.Path = ref.Path + url.RawPath = ref.RawPath + url.RawQuery = ref.RawQuery + case strings.HasPrefix(ref.Path, "/"): + // The "abs_path" case. + url.Path = ref.Path + url.RawPath = ref.RawPath + url.RawQuery = ref.RawQuery + default: + // The "rel_path" case. + path := resolvePath(base.Path, ref.Path) + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + url.Path = path + url.RawPath = url.Path + url.RawQuery = ref.RawQuery + if ref.RawQuery != "" { + url.RawPath += "?" + url.RawQuery + } + } + + url.Fragment = ref.Fragment + } + url.Raw = url.String() + return url +} + +// Query parses RawQuery and returns the corresponding values. +func (u *URL) Query() Values { + v, _ := ParseQuery(u.RawQuery) + return v +} + +// EncodedPath returns the URL's path in "URL path encoded" form. +func (u *URL) EncodedPath() string { + return escape(u.Path, encodePath) +} diff --git a/src/pkg/url/url_test.go b/src/pkg/url/url_test.go new file mode 100644 index 000000000..af394d4fb --- /dev/null +++ b/src/pkg/url/url_test.go @@ -0,0 +1,698 @@ +// 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 url + +import ( + "fmt" + "os" + "reflect" + "testing" +) + +// TODO(rsc): +// test Unescape +// test Escape +// test Parse + +type URLTest struct { + in string + out *URL + roundtrip string // expected result of reserializing the URL; empty means same as "in". +} + +var urltests = []URLTest{ + // no path + { + "http://www.google.com", + &URL{ + Raw: "http://www.google.com", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + }, + "", + }, + // path + { + "http://www.google.com/", + &URL{ + Raw: "http://www.google.com/", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/", + Path: "/", + }, + "", + }, + // path with hex escaping + { + "http://www.google.com/file%20one%26two", + &URL{ + Raw: "http://www.google.com/file%20one%26two", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/file%20one%26two", + Path: "/file one&two", + }, + "http://www.google.com/file%20one&two", + }, + // user + { + "ftp://webmaster@www.google.com/", + &URL{ + Raw: "ftp://webmaster@www.google.com/", + Scheme: "ftp", + RawAuthority: "webmaster@www.google.com", + RawUserinfo: "webmaster", + Host: "www.google.com", + RawPath: "/", + Path: "/", + }, + "", + }, + // escape sequence in username + { + "ftp://john%20doe@www.google.com/", + &URL{ + Raw: "ftp://john%20doe@www.google.com/", + Scheme: "ftp", + RawAuthority: "john%20doe@www.google.com", + RawUserinfo: "john%20doe", + Host: "www.google.com", + RawPath: "/", + Path: "/", + }, + "ftp://john%20doe@www.google.com/", + }, + // query + { + "http://www.google.com/?q=go+language", + &URL{ + Raw: "http://www.google.com/?q=go+language", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/?q=go+language", + Path: "/", + RawQuery: "q=go+language", + }, + "", + }, + // query with hex escaping: NOT parsed + { + "http://www.google.com/?q=go%20language", + &URL{ + Raw: "http://www.google.com/?q=go%20language", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/?q=go%20language", + Path: "/", + RawQuery: "q=go%20language", + }, + "", + }, + // %20 outside query + { + "http://www.google.com/a%20b?q=c+d", + &URL{ + Raw: "http://www.google.com/a%20b?q=c+d", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/a%20b?q=c+d", + Path: "/a b", + RawQuery: "q=c+d", + }, + "", + }, + // path without leading /, so no query parsing + { + "http:www.google.com/?q=go+language", + &URL{ + Raw: "http:www.google.com/?q=go+language", + Scheme: "http", + RawPath: "www.google.com/?q=go+language", + Path: "www.google.com/?q=go+language", + OpaquePath: true, + }, + "http:www.google.com/?q=go+language", + }, + // path without leading /, so no query parsing + { + "http:%2f%2fwww.google.com/?q=go+language", + &URL{ + Raw: "http:%2f%2fwww.google.com/?q=go+language", + Scheme: "http", + RawPath: "%2f%2fwww.google.com/?q=go+language", + Path: "//www.google.com/?q=go+language", + OpaquePath: true, + }, + "http:%2f/www.google.com/?q=go+language", + }, + // non-authority + { + "mailto:/webmaster@golang.org", + &URL{ + Raw: "mailto:/webmaster@golang.org", + Scheme: "mailto", + RawPath: "/webmaster@golang.org", + Path: "/webmaster@golang.org", + }, + "", + }, + // non-authority + { + "mailto:webmaster@golang.org", + &URL{ + Raw: "mailto:webmaster@golang.org", + Scheme: "mailto", + RawPath: "webmaster@golang.org", + Path: "webmaster@golang.org", + OpaquePath: true, + }, + "", + }, + // unescaped :// in query should not create a scheme + { + "/foo?query=http://bad", + &URL{ + Raw: "/foo?query=http://bad", + RawPath: "/foo?query=http://bad", + Path: "/foo", + RawQuery: "query=http://bad", + }, + "", + }, + // leading // without scheme should create an authority + { + "//foo", + &URL{ + RawAuthority: "foo", + Raw: "//foo", + Host: "foo", + Scheme: "", + RawPath: "", + Path: "", + }, + "", + }, + // leading // without scheme, with userinfo, path, and query + { + "//user@foo/path?a=b", + &URL{ + Raw: "//user@foo/path?a=b", + RawAuthority: "user@foo", + RawUserinfo: "user", + Scheme: "", + RawPath: "/path?a=b", + Path: "/path", + RawQuery: "a=b", + Host: "foo", + }, + "", + }, + // Three leading slashes isn't an authority, but doesn't return an error. + // (We can't return an error, as this code is also used via + // ServeHTTP -> ReadRequest -> Parse, which is arguably a + // different URL parsing context, but currently shares the + // same codepath) + { + "///threeslashes", + &URL{ + RawAuthority: "", + Raw: "///threeslashes", + Host: "", + Scheme: "", + RawPath: "///threeslashes", + Path: "///threeslashes", + }, + "", + }, + { + "http://user:password@google.com", + &URL{ + Raw: "http://user:password@google.com", + Scheme: "http", + RawAuthority: "user:password@google.com", + RawUserinfo: "user:password", + Host: "google.com", + }, + "http://user:******@google.com", + }, + { + "http://user:longerpass@google.com", + &URL{ + Raw: "http://user:longerpass@google.com", + Scheme: "http", + RawAuthority: "user:longerpass@google.com", + RawUserinfo: "user:longerpass", + Host: "google.com", + }, + "http://user:******@google.com", + }, +} + +var urlnofragtests = []URLTest{ + { + "http://www.google.com/?q=go+language#foo", + &URL{ + Raw: "http://www.google.com/?q=go+language#foo", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/?q=go+language#foo", + Path: "/", + RawQuery: "q=go+language#foo", + }, + "", + }, +} + +var urlfragtests = []URLTest{ + { + "http://www.google.com/?q=go+language#foo", + &URL{ + Raw: "http://www.google.com/?q=go+language#foo", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/?q=go+language#foo", + Path: "/", + RawQuery: "q=go+language", + Fragment: "foo", + }, + "", + }, + { + "http://www.google.com/?q=go+language#foo%26bar", + &URL{ + Raw: "http://www.google.com/?q=go+language#foo%26bar", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/?q=go+language#foo%26bar", + Path: "/", + RawQuery: "q=go+language", + Fragment: "foo&bar", + }, + "http://www.google.com/?q=go+language#foo&bar", + }, +} + +// more useful string for debugging than fmt's struct printer +func ufmt(u *URL) string { + return fmt.Sprintf("raw=%q, scheme=%q, rawpath=%q, auth=%q, userinfo=%q, host=%q, path=%q, rawq=%q, frag=%q", + u.Raw, u.Scheme, u.RawPath, u.RawAuthority, u.RawUserinfo, + u.Host, u.Path, u.RawQuery, u.Fragment) +} + +func DoTest(t *testing.T, parse func(string) (*URL, os.Error), name string, tests []URLTest) { + for _, tt := range tests { + u, err := parse(tt.in) + if err != nil { + t.Errorf("%s(%q) returned error %s", name, tt.in, err) + continue + } + if !reflect.DeepEqual(u, tt.out) { + t.Errorf("%s(%q):\n\thave %v\n\twant %v\n", + name, tt.in, ufmt(u), ufmt(tt.out)) + } + } +} + +func TestParse(t *testing.T) { + DoTest(t, Parse, "Parse", urltests) + DoTest(t, Parse, "Parse", urlnofragtests) +} + +func TestParseWithReference(t *testing.T) { + DoTest(t, ParseWithReference, "ParseWithReference", urltests) + DoTest(t, ParseWithReference, "ParseWithReference", urlfragtests) +} + +const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path" + +var parseRequestUrlTests = []struct { + url string + expectedValid bool +}{ + {"http://foo.com", true}, + {"http://foo.com/", true}, + {"http://foo.com/path", true}, + {"/", true}, + {pathThatLooksSchemeRelative, true}, + {"//not.a.user@%66%6f%6f.com/just/a/path/also", true}, + {"foo.html", false}, + {"../dir/", false}, +} + +func TestParseRequest(t *testing.T) { + for _, test := range parseRequestUrlTests { + _, err := ParseRequest(test.url) + valid := err == nil + if valid != test.expectedValid { + t.Errorf("Expected valid=%v for %q; got %v", test.expectedValid, test.url, valid) + } + } + + url, err := ParseRequest(pathThatLooksSchemeRelative) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if url.Path != pathThatLooksSchemeRelative { + t.Errorf("Expected path %q; got %q", pathThatLooksSchemeRelative, url.Path) + } +} + +func DoTestString(t *testing.T, parse func(string) (*URL, os.Error), name string, tests []URLTest) { + for _, tt := range tests { + u, err := parse(tt.in) + if err != nil { + t.Errorf("%s(%q) returned error %s", name, tt.in, err) + continue + } + s := u.String() + expected := tt.in + if len(tt.roundtrip) > 0 { + expected = tt.roundtrip + } + if s != expected { + t.Errorf("%s(%q).String() == %q (expected %q)", name, tt.in, s, expected) + } + } +} + +func TestURLString(t *testing.T) { + DoTestString(t, Parse, "Parse", urltests) + DoTestString(t, Parse, "Parse", urlnofragtests) + DoTestString(t, ParseWithReference, "ParseWithReference", urltests) + DoTestString(t, ParseWithReference, "ParseWithReference", urlfragtests) +} + +type EscapeTest struct { + in string + out string + err os.Error +} + +var unescapeTests = []EscapeTest{ + { + "", + "", + nil, + }, + { + "abc", + "abc", + nil, + }, + { + "1%41", + "1A", + nil, + }, + { + "1%41%42%43", + "1ABC", + nil, + }, + { + "%4a", + "J", + nil, + }, + { + "%6F", + "o", + nil, + }, + { + "%", // not enough characters after % + "", + EscapeError("%"), + }, + { + "%a", // not enough characters after % + "", + EscapeError("%a"), + }, + { + "%1", // not enough characters after % + "", + EscapeError("%1"), + }, + { + "123%45%6", // not enough characters after % + "", + EscapeError("%6"), + }, + { + "%zzzzz", // invalid hex digits + "", + EscapeError("%zz"), + }, +} + +func TestUnescape(t *testing.T) { + for _, tt := range unescapeTests { + actual, err := QueryUnescape(tt.in) + if actual != tt.out || (err != nil) != (tt.err != nil) { + t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", tt.in, actual, err, tt.out, tt.err) + } + } +} + +var escapeTests = []EscapeTest{ + { + "", + "", + nil, + }, + { + "abc", + "abc", + nil, + }, + { + "one two", + "one+two", + nil, + }, + { + "10%", + "10%25", + nil, + }, + { + " ?&=#+%!<>#\"{}|\\^[]`☺\t", + "+%3F%26%3D%23%2B%25!%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09", + nil, + }, +} + +func TestEscape(t *testing.T) { + for _, tt := range escapeTests { + actual := QueryEscape(tt.in) + if tt.out != actual { + t.Errorf("QueryEscape(%q) = %q, want %q", tt.in, actual, tt.out) + } + + // for bonus points, verify that escape:unescape is an identity. + roundtrip, err := QueryUnescape(actual) + if roundtrip != tt.in || err != nil { + t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]") + } + } +} + +type UserinfoTest struct { + User string + Password string + Raw string +} + +var userinfoTests = []UserinfoTest{ + {"user", "password", "user:password"}, + {"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./", + "foo%3Abar:~!%40%23$%25%5E&*()_+%7B%7D%7C%5B%5D%5C-=%60%3A;'%22%3C%3E?,.%2F"}, +} + +func TestEscapeUserinfo(t *testing.T) { + for _, tt := range userinfoTests { + if raw := EscapeUserinfo(tt.User, tt.Password); raw != tt.Raw { + t.Errorf("EscapeUserinfo(%q, %q) = %q, want %q", tt.User, tt.Password, raw, tt.Raw) + } + } +} + +func TestUnescapeUserinfo(t *testing.T) { + for _, tt := range userinfoTests { + if user, pass, err := UnescapeUserinfo(tt.Raw); user != tt.User || pass != tt.Password || err != nil { + t.Errorf("UnescapeUserinfo(%q) = %q, %q, %v, want %q, %q, nil", tt.Raw, user, pass, err, tt.User, tt.Password) + } + } +} + +type EncodeQueryTest struct { + m Values + expected string + expected1 string +} + +var encodeQueryTests = []EncodeQueryTest{ + {nil, "", ""}, + {Values{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"}, + {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"}, +} + +func TestEncodeQuery(t *testing.T) { + for _, tt := range encodeQueryTests { + if q := tt.m.Encode(); q != tt.expected && q != tt.expected1 { + t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected) + } + } +} + +var resolvePathTests = []struct { + base, ref, expected string +}{ + {"a/b", ".", "a/"}, + {"a/b", "c", "a/c"}, + {"a/b", "..", ""}, + {"a/", "..", ""}, + {"a/", "../..", ""}, + {"a/b/c", "..", "a/"}, + {"a/b/c", "../d", "a/d"}, + {"a/b/c", ".././d", "a/d"}, + {"a/b", "./..", ""}, + {"a/./b", ".", "a/./"}, + {"a/../", ".", "a/../"}, + {"a/.././b", "c", "a/.././c"}, +} + +func TestResolvePath(t *testing.T) { + for _, test := range resolvePathTests { + got := resolvePath(test.base, test.ref) + if got != test.expected { + t.Errorf("For %q + %q got %q; expected %q", test.base, test.ref, got, test.expected) + } + } +} + +var resolveReferenceTests = []struct { + base, rel, expected string +}{ + // Absolute URL references + {"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"}, + {"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"}, + {"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"}, + + // Path-absolute references + {"http://foo.com/bar", "/baz", "http://foo.com/baz"}, + {"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"}, + {"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"}, + + // Scheme-relative + {"https://foo.com/bar?a=b", "//bar.com/quux", "https://bar.com/quux"}, + + // Path-relative references: + + // ... current directory + {"http://foo.com", ".", "http://foo.com/"}, + {"http://foo.com/bar", ".", "http://foo.com/"}, + {"http://foo.com/bar/", ".", "http://foo.com/bar/"}, + + // ... going down + {"http://foo.com", "bar", "http://foo.com/bar"}, + {"http://foo.com/", "bar", "http://foo.com/bar"}, + {"http://foo.com/bar/baz", "quux", "http://foo.com/bar/quux"}, + + // ... going up + {"http://foo.com/bar/baz", "../quux", "http://foo.com/quux"}, + {"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"}, + {"http://foo.com/bar", "..", "http://foo.com/"}, + {"http://foo.com/bar/baz", "./..", "http://foo.com/"}, + + // "." and ".." in the base aren't special + {"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/./dotdot/../baz"}, + + // Triple dot isn't special + {"http://foo.com/bar", "...", "http://foo.com/..."}, + + // Fragment + {"http://foo.com/bar", ".#frag", "http://foo.com/#frag"}, +} + +func TestResolveReference(t *testing.T) { + mustParse := func(url string) *URL { + u, err := ParseWithReference(url) + if err != nil { + t.Fatalf("Expected URL to parse: %q, got error: %v", url, err) + } + return u + } + for _, test := range resolveReferenceTests { + base := mustParse(test.base) + rel := mustParse(test.rel) + url := base.ResolveReference(rel) + urlStr := url.String() + if urlStr != test.expected { + t.Errorf("Resolving %q + %q != %q; got %q", test.base, test.rel, test.expected, urlStr) + } + } + + // Test that new instances are returned. + base := mustParse("http://foo.com/") + abs := base.ResolveReference(mustParse(".")) + if base == abs { + t.Errorf("Expected no-op reference to return new URL instance.") + } + barRef := mustParse("http://bar.com/") + abs = base.ResolveReference(barRef) + if abs == barRef { + t.Errorf("Expected resolution of absolute reference to return new URL instance.") + } + + // Test the convenience wrapper too + base = mustParse("http://foo.com/path/one/") + abs, _ = base.Parse("../two") + expected := "http://foo.com/path/two" + if abs.String() != expected { + t.Errorf("Parse wrapper got %q; expected %q", abs.String(), expected) + } + _, err := base.Parse("") + if err == nil { + t.Errorf("Expected an error from Parse wrapper parsing an empty string.") + } + +} + +func TestQueryValues(t *testing.T) { + u, _ := Parse("http://x.com?foo=bar&bar=1&bar=2") + v := u.Query() + if len(v) != 2 { + t.Errorf("got %d keys in Query values, want 2", len(v)) + } + if g, e := v.Get("foo"), "bar"; g != e { + t.Errorf("Get(foo) = %q, want %q", g, e) + } + // Case sensitive: + if g, e := v.Get("Foo"), ""; g != e { + t.Errorf("Get(Foo) = %q, want %q", g, e) + } + if g, e := v.Get("bar"), "1"; g != e { + t.Errorf("Get(bar) = %q, want %q", g, e) + } + if g, e := v.Get("baz"), ""; g != e { + t.Errorf("Get(baz) = %q, want %q", g, e) + } + v.Del("bar") + if g, e := v.Get("bar"), ""; g != e { + t.Errorf("second Get(bar) = %q, want %q", g, e) + } +} diff --git a/src/pkg/utf8/utf8.go b/src/pkg/utf8/utf8.go index f542358d6..8910e17d7 100644 --- a/src/pkg/utf8/utf8.go +++ b/src/pkg/utf8/utf8.go @@ -16,22 +16,22 @@ const ( ) const ( - _T1 = 0x00 // 0000 0000 - _Tx = 0x80 // 1000 0000 - _T2 = 0xC0 // 1100 0000 - _T3 = 0xE0 // 1110 0000 - _T4 = 0xF0 // 1111 0000 - _T5 = 0xF8 // 1111 1000 - - _Maskx = 0x3F // 0011 1111 - _Mask2 = 0x1F // 0001 1111 - _Mask3 = 0x0F // 0000 1111 - _Mask4 = 0x07 // 0000 0111 - - _Rune1Max = 1<<7 - 1 - _Rune2Max = 1<<11 - 1 - _Rune3Max = 1<<16 - 1 - _Rune4Max = 1<<21 - 1 + t1 = 0x00 // 0000 0000 + tx = 0x80 // 1000 0000 + t2 = 0xC0 // 1100 0000 + t3 = 0xE0 // 1110 0000 + t4 = 0xF0 // 1111 0000 + t5 = 0xF8 // 1111 1000 + + maskx = 0x3F // 0011 1111 + mask2 = 0x1F // 0001 1111 + mask3 = 0x0F // 0000 1111 + mask4 = 0x07 // 0000 0111 + + rune1Max = 1<<7 - 1 + rune2Max = 1<<11 - 1 + rune3Max = 1<<16 - 1 + rune4Max = 1<<21 - 1 ) func decodeRuneInternal(p []byte) (rune, size int, short bool) { @@ -42,12 +42,12 @@ func decodeRuneInternal(p []byte) (rune, size int, short bool) { c0 := p[0] // 1-byte, 7-bit sequence? - if c0 < _Tx { + if c0 < tx { return int(c0), 1, false } // unexpected continuation byte? - if c0 < _T2 { + if c0 < t2 { return RuneError, 1, false } @@ -56,14 +56,14 @@ func decodeRuneInternal(p []byte) (rune, size int, short bool) { return RuneError, 1, true } c1 := p[1] - if c1 < _Tx || _T2 <= c1 { + if c1 < tx || t2 <= c1 { return RuneError, 1, false } // 2-byte, 11-bit sequence? - if c0 < _T3 { - rune = int(c0&_Mask2)<<6 | int(c1&_Maskx) - if rune <= _Rune1Max { + if c0 < t3 { + rune = int(c0&mask2)<<6 | int(c1&maskx) + if rune <= rune1Max { return RuneError, 1, false } return rune, 2, false @@ -74,14 +74,14 @@ func decodeRuneInternal(p []byte) (rune, size int, short bool) { return RuneError, 1, true } c2 := p[2] - if c2 < _Tx || _T2 <= c2 { + if c2 < tx || t2 <= c2 { return RuneError, 1, false } // 3-byte, 16-bit sequence? - if c0 < _T4 { - rune = int(c0&_Mask3)<<12 | int(c1&_Maskx)<<6 | int(c2&_Maskx) - if rune <= _Rune2Max { + if c0 < t4 { + rune = int(c0&mask3)<<12 | int(c1&maskx)<<6 | int(c2&maskx) + if rune <= rune2Max { return RuneError, 1, false } return rune, 3, false @@ -92,14 +92,14 @@ func decodeRuneInternal(p []byte) (rune, size int, short bool) { return RuneError, 1, true } c3 := p[3] - if c3 < _Tx || _T2 <= c3 { + if c3 < tx || t2 <= c3 { return RuneError, 1, false } // 4-byte, 21-bit sequence? - if c0 < _T5 { - rune = int(c0&_Mask4)<<18 | int(c1&_Maskx)<<12 | int(c2&_Maskx)<<6 | int(c3&_Maskx) - if rune <= _Rune3Max { + if c0 < t5 { + rune = int(c0&mask4)<<18 | int(c1&maskx)<<12 | int(c2&maskx)<<6 | int(c3&maskx) + if rune <= rune3Max { return RuneError, 1, false } return rune, 4, false @@ -117,12 +117,12 @@ func decodeRuneInStringInternal(s string) (rune, size int, short bool) { c0 := s[0] // 1-byte, 7-bit sequence? - if c0 < _Tx { + if c0 < tx { return int(c0), 1, false } // unexpected continuation byte? - if c0 < _T2 { + if c0 < t2 { return RuneError, 1, false } @@ -131,14 +131,14 @@ func decodeRuneInStringInternal(s string) (rune, size int, short bool) { return RuneError, 1, true } c1 := s[1] - if c1 < _Tx || _T2 <= c1 { + if c1 < tx || t2 <= c1 { return RuneError, 1, false } // 2-byte, 11-bit sequence? - if c0 < _T3 { - rune = int(c0&_Mask2)<<6 | int(c1&_Maskx) - if rune <= _Rune1Max { + if c0 < t3 { + rune = int(c0&mask2)<<6 | int(c1&maskx) + if rune <= rune1Max { return RuneError, 1, false } return rune, 2, false @@ -149,14 +149,14 @@ func decodeRuneInStringInternal(s string) (rune, size int, short bool) { return RuneError, 1, true } c2 := s[2] - if c2 < _Tx || _T2 <= c2 { + if c2 < tx || t2 <= c2 { return RuneError, 1, false } // 3-byte, 16-bit sequence? - if c0 < _T4 { - rune = int(c0&_Mask3)<<12 | int(c1&_Maskx)<<6 | int(c2&_Maskx) - if rune <= _Rune2Max { + if c0 < t4 { + rune = int(c0&mask3)<<12 | int(c1&maskx)<<6 | int(c2&maskx) + if rune <= rune2Max { return RuneError, 1, false } return rune, 3, false @@ -167,14 +167,14 @@ func decodeRuneInStringInternal(s string) (rune, size int, short bool) { return RuneError, 1, true } c3 := s[3] - if c3 < _Tx || _T2 <= c3 { + if c3 < tx || t2 <= c3 { return RuneError, 1, false } // 4-byte, 21-bit sequence? - if c0 < _T5 { - rune = int(c0&_Mask4)<<18 | int(c1&_Maskx)<<12 | int(c2&_Maskx)<<6 | int(c3&_Maskx) - if rune <= _Rune3Max { + if c0 < t5 { + rune = int(c0&mask4)<<18 | int(c1&maskx)<<12 | int(c2&maskx)<<6 | int(c3&maskx) + if rune <= rune3Max { return RuneError, 1, false } return rune, 4, false @@ -279,13 +279,13 @@ func DecodeLastRuneInString(s string) (rune, size int) { // RuneLen returns the number of bytes required to encode the rune. func RuneLen(rune int) int { switch { - case rune <= _Rune1Max: + case rune <= rune1Max: return 1 - case rune <= _Rune2Max: + case rune <= rune2Max: return 2 - case rune <= _Rune3Max: + case rune <= rune3Max: return 3 - case rune <= _Rune4Max: + case rune <= rune4Max: return 4 } return -1 @@ -297,14 +297,14 @@ func EncodeRune(p []byte, rune int) int { // Negative values are erroneous. Making it unsigned addresses the problem. r := uint(rune) - if r <= _Rune1Max { + if r <= rune1Max { p[0] = byte(r) return 1 } - if r <= _Rune2Max { - p[0] = _T2 | byte(r>>6) - p[1] = _Tx | byte(r)&_Maskx + if r <= rune2Max { + p[0] = t2 | byte(r>>6) + p[1] = tx | byte(r)&maskx return 2 } @@ -312,17 +312,17 @@ func EncodeRune(p []byte, rune int) int { r = RuneError } - if r <= _Rune3Max { - p[0] = _T3 | byte(r>>12) - p[1] = _Tx | byte(r>>6)&_Maskx - p[2] = _Tx | byte(r)&_Maskx + if r <= rune3Max { + p[0] = t3 | byte(r>>12) + p[1] = tx | byte(r>>6)&maskx + p[2] = tx | byte(r)&maskx return 3 } - p[0] = _T4 | byte(r>>18) - p[1] = _Tx | byte(r>>12)&_Maskx - p[2] = _Tx | byte(r>>6)&_Maskx - p[3] = _Tx | byte(r)&_Maskx + p[0] = t4 | byte(r>>18) + p[1] = tx | byte(r>>12)&maskx + p[2] = tx | byte(r>>6)&maskx + p[3] = tx | byte(r)&maskx return 4 } diff --git a/src/pkg/websocket/client.go b/src/pkg/websocket/client.go index f24c46360..74bede424 100644 --- a/src/pkg/websocket/client.go +++ b/src/pkg/websocket/client.go @@ -15,6 +15,7 @@ import ( "os" "rand" "strings" + "url" ) type ProtocolError struct { @@ -99,10 +100,10 @@ A trivial example client: // use msg[0:n] } */ -func Dial(url, protocol, origin string) (ws *Conn, err os.Error) { +func Dial(url_, protocol, origin string) (ws *Conn, err os.Error) { var client net.Conn - parsedUrl, err := http.ParseURL(url) + parsedUrl, err := url.Parse(url_) if err != nil { goto Error } @@ -121,14 +122,14 @@ func Dial(url, protocol, origin string) (ws *Conn, err os.Error) { goto Error } - ws, err = newClient(parsedUrl.RawPath, parsedUrl.Host, origin, url, protocol, client, handshake) + ws, err = newClient(parsedUrl.RawPath, parsedUrl.Host, origin, url_, protocol, client, handshake) if err != nil { goto Error } return Error: - return nil, &DialError{url, protocol, origin, err} + return nil, &DialError{url_, protocol, origin, err} } /* diff --git a/src/pkg/websocket/websocket_test.go b/src/pkg/websocket/websocket_test.go index 84788b416..71c3c8514 100644 --- a/src/pkg/websocket/websocket_test.go +++ b/src/pkg/websocket/websocket_test.go @@ -15,6 +15,7 @@ import ( "net" "sync" "testing" + "url" ) var serverAddr string @@ -155,9 +156,9 @@ func TestHTTP(t *testing.T) { t.Error("Get: unexpected success") return } - urlerr, ok := err.(*http.URLError) + urlerr, ok := err.(*url.Error) if !ok { - t.Errorf("Get: not URLError %#v", err) + t.Errorf("Get: not url.Error %#v", err) return } if urlerr.Error != io.ErrUnexpectedEOF { diff --git a/src/pkg/xml/marshal.go b/src/pkg/xml/marshal.go index f6e5bf5cd..ea421c1b1 100644 --- a/src/pkg/xml/marshal.go +++ b/src/pkg/xml/marshal.go @@ -177,7 +177,7 @@ func (p *printer) marshalValue(val reflect.Value, name string) os.Error { case "": case "chardata": if tk := f.Type.Kind(); tk == reflect.String { - p.Write([]byte(val.Field(i).String())) + Escape(p, []byte(val.Field(i).String())) } else if tk == reflect.Slice { if elem, ok := val.Field(i).Interface().([]byte); ok { Escape(p, elem) diff --git a/src/pkg/xml/marshal_test.go b/src/pkg/xml/marshal_test.go index 77b2e726d..5b972fafe 100644 --- a/src/pkg/xml/marshal_test.go +++ b/src/pkg/xml/marshal_test.go @@ -57,6 +57,11 @@ type Domain struct { Name []byte `xml:"chardata"` } +type Book struct { + XMLName Name `xml:"book"` + Title string `xml:"chardata"` +} + type SecretAgent struct { XMLName Name `xml:"agent"` Handle string `xml:"attr"` @@ -113,6 +118,7 @@ var marshalTests = []struct { {Value: &Port{Number: "443"}, ExpectXML: `443`}, {Value: &Port{Type: ""}, ExpectXML: ``}, {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `google.com&friends`}, + {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `Pride & Prejudice`}, {Value: atomValue, ExpectXML: atomXml}, { Value: &Ship{ diff --git a/src/run.bash b/src/run.bash index ae79a0cec..a9689bf15 100755 --- a/src/run.bash +++ b/src/run.bash @@ -49,20 +49,20 @@ GOMAXPROCS=10 gomake testshort time gomake test ) || exit $? -[ "$GOARCH" == arm ] || +[ "$CGO_ENABLED" != 1 ] || [ "$GOHOSTOS" == windows ] || (xcd ../misc/cgo/stdio gomake clean ./test.bash ) || exit $? -[ "$GOARCH" == arm ] || +[ "$CGO_ENABLED" != 1 ] || (xcd ../misc/cgo/life gomake clean ./test.bash ) || exit $? -[ "$GOARCH" == arm ] || +[ "$CGO_ENABLED" != 1 ] || [ "$GOHOSTOS" == windows ] || (xcd ../misc/cgo/test gomake clean diff --git a/src/version.bash b/src/version.bash index ce5a9969a..fc899e2e3 100755 --- a/src/version.bash +++ b/src/version.bash @@ -3,9 +3,18 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Check that we can use 'hg' -if ! hg version > /dev/null 2>&1; then - echo 'hg not installed' 1>&2 +GOROOT=$(dirname $0)/.. + +# If a version file created by -save is available, use it +if [ -f "$GOROOT/VERSION" ]; then + cat $GOROOT/VERSION + exit 0 +fi + +# Otherwise, if hg doesn't work for whatever reason, fail +if [ ! -d "$GOROOT/.hg" ] || ! hg version > /dev/null 2>&1; then + echo 'Unable to report version: hg and VERSION file missing' 1>&2 + echo 'Generate VERSION with `src/version.bash -save` while hg is usable' 1>&2 exit 2 fi @@ -34,5 +43,8 @@ if [ "$TAG" != "" ]; then VERSION="$TAG $VERSION" fi -echo $VERSION - +if [ "$1" = "-save" ]; then + echo $VERSION > $GOROOT/VERSION +else + echo $VERSION +fi -- cgit v1.2.3