1// Copyright 2012 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build ignore
6
7//
8// usage:
9//
10// go run makeisprint.go -output isprint.go
11//
12
13package main
14
15import (
16	"bytes"
17	"flag"
18	"fmt"
19	"go/format"
20	"log"
21	"os"
22	"slices"
23	"unicode"
24)
25
26var filename = flag.String("output", "isprint.go", "output file name")
27
28var (
29	range16  []uint16
30	except16 []uint16
31	range32  []uint32
32	except32 []uint32
33)
34
35func isPrint(r rune) bool {
36	// Same algorithm, either on uint16 or uint32 value.
37	// First, find first i such that rang[i] >= x.
38	// This is the index of either the start or end of a pair that might span x.
39	// The start is even (rang[i&^1]) and the end is odd (rang[i|1]).
40	// If we find x in a range, make sure x is not in exception list.
41
42	if 0 <= r && r < 1<<16 {
43		rr, rang, except := uint16(r), range16, except16
44		i, _ := slices.BinarySearch(rang, rr)
45		if i >= len(rang) || rr < rang[i&^1] || rang[i|1] < rr {
46			return false
47		}
48		_, found := slices.BinarySearch(except, rr)
49		return !found
50	}
51
52	rr, rang, except := uint32(r), range32, except32
53	i, _ := slices.BinarySearch(rang, rr)
54	if i >= len(rang) || rr < rang[i&^1] || rang[i|1] < rr {
55		return false
56	}
57	_, found := slices.BinarySearch(except, rr)
58	return !found
59}
60
61func scan(min, max rune) (rang, except []uint32) {
62	lo := rune(-1)
63	for i := min; ; i++ {
64		if (i > max || !unicode.IsPrint(i)) && lo >= 0 {
65			// End range, but avoid flip flop.
66			if i+1 <= max && unicode.IsPrint(i+1) {
67				except = append(except, uint32(i))
68				continue
69			}
70			rang = append(rang, uint32(lo), uint32(i-1))
71			lo = -1
72		}
73		if i > max {
74			break
75		}
76		if lo < 0 && unicode.IsPrint(i) {
77			lo = i
78		}
79	}
80	return
81}
82
83func to16(x []uint32) []uint16 {
84	var y []uint16
85	for _, v := range x {
86		if uint32(uint16(v)) != v {
87			panic("bad 32->16 conversion")
88		}
89		y = append(y, uint16(v))
90	}
91	return y
92}
93
94func main() {
95	flag.Parse()
96
97	rang, except := scan(0, 0xFFFF)
98	range16 = to16(rang)
99	except16 = to16(except)
100	range32, except32 = scan(0x10000, unicode.MaxRune)
101
102	for i := rune(0); i <= unicode.MaxRune; i++ {
103		if isPrint(i) != unicode.IsPrint(i) {
104			log.Fatalf("%U: isPrint=%v, want %v\n", i, isPrint(i), unicode.IsPrint(i))
105		}
106	}
107
108	var buf bytes.Buffer
109
110	fmt.Fprintf(&buf, `// Copyright 2013 The Go Authors. All rights reserved.
111// Use of this source code is governed by a BSD-style
112// license that can be found in the LICENSE file.`+"\n\n")
113	fmt.Fprintf(&buf, "// Code generated by go run makeisprint.go -output isprint.go; DO NOT EDIT.\n\n")
114	fmt.Fprintf(&buf, "package strconv\n\n")
115
116	fmt.Fprintf(&buf, "// (%d+%d+%d)*2 + (%d)*4 = %d bytes\n\n",
117		len(range16), len(except16), len(except32),
118		len(range32),
119		(len(range16)+len(except16)+len(except32))*2+
120			(len(range32))*4)
121
122	fmt.Fprintf(&buf, "var isPrint16 = []uint16{\n")
123	for i := 0; i < len(range16); i += 2 {
124		fmt.Fprintf(&buf, "\t%#04x, %#04x,\n", range16[i], range16[i+1])
125	}
126	fmt.Fprintf(&buf, "}\n\n")
127
128	fmt.Fprintf(&buf, "var isNotPrint16 = []uint16{\n")
129	for _, r := range except16 {
130		fmt.Fprintf(&buf, "\t%#04x,\n", r)
131	}
132	fmt.Fprintf(&buf, "}\n\n")
133
134	fmt.Fprintf(&buf, "var isPrint32 = []uint32{\n")
135	for i := 0; i < len(range32); i += 2 {
136		fmt.Fprintf(&buf, "\t%#06x, %#06x,\n", range32[i], range32[i+1])
137	}
138	fmt.Fprintf(&buf, "}\n\n")
139
140	fmt.Fprintf(&buf, "var isNotPrint32 = []uint16{ // add 0x10000 to each entry\n")
141	for _, r := range except32 {
142		if r >= 0x20000 {
143			log.Fatalf("%U too big for isNotPrint32\n", r)
144		}
145		fmt.Fprintf(&buf, "\t%#04x,\n", r-0x10000)
146	}
147	fmt.Fprintf(&buf, "}\n\n")
148
149	// The list of graphic but not "printable" runes is short. Just make one easy table.
150	fmt.Fprintf(&buf, "// isGraphic lists the graphic runes not matched by IsPrint.\n")
151	fmt.Fprintf(&buf, "var isGraphic = []uint16{\n")
152	for r := rune(0); r <= unicode.MaxRune; r++ {
153		if unicode.IsPrint(r) != unicode.IsGraphic(r) {
154			// Sanity check.
155			if !unicode.IsGraphic(r) {
156				log.Fatalf("%U is printable but not graphic\n", r)
157			}
158			if r > 0xFFFF { // We expect only 16-bit values.
159				log.Fatalf("%U too big for isGraphic\n", r)
160			}
161			fmt.Fprintf(&buf, "\t%#04x,\n", r)
162		}
163	}
164	fmt.Fprintf(&buf, "}\n")
165
166	data, err := format.Source(buf.Bytes())
167	if err != nil {
168		log.Fatal(err)
169	}
170	err = os.WriteFile(*filename, data, 0644)
171	if err != nil {
172		log.Fatal(err)
173	}
174}
175