makeisprint.mx raw

   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  
  13  package main
  14  
  15  import (
  16  	"bytes"
  17  	"flag"
  18  	"fmt"
  19  	"go/format"
  20  	"log"
  21  	"os"
  22  	"slices"
  23  	"unicode"
  24  )
  25  
  26  var filename = flag.String("output", "isprint.go", "output file name")
  27  
  28  var (
  29  	range16  []uint16
  30  	except16 []uint16
  31  	range32  []uint32
  32  	except32 []uint32
  33  )
  34  
  35  func 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  
  61  func 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  
  83  func 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  
  94  func 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