Source file src/strconv/makeisprint.go

     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  

View as plain text