1// Copyright (c) 2017, Google Inc. 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15//go:build ignore 16 17// embed_test_data generates a C++ source file which exports a function, 18// GetTestData, which looks up the specified data files. 19package main 20 21import ( 22 "bytes" 23 "flag" 24 "fmt" 25 "os" 26 "strings" 27) 28 29var fileList = flag.String("file-list", "", "if not empty, the path to a file containing a newline-separated list of files, to work around Windows command-line limits") 30 31func quote(in []byte) string { 32 var lastWasHex bool 33 var buf bytes.Buffer 34 buf.WriteByte('"') 35 for _, b := range in { 36 var wasHex bool 37 switch b { 38 case '\a': 39 buf.WriteString(`\a`) 40 case '\b': 41 buf.WriteString(`\b`) 42 case '\f': 43 buf.WriteString(`\f`) 44 case '\n': 45 buf.WriteString(`\n`) 46 case '\r': 47 buf.WriteString(`\r`) 48 case '\t': 49 buf.WriteString(`\t`) 50 case '\v': 51 buf.WriteString(`\v`) 52 case '"': 53 buf.WriteString(`\"`) 54 case '\\': 55 buf.WriteString(`\\`) 56 default: 57 // Emit printable ASCII characters, [32, 126], as-is to minimize 58 // file size. However, if the previous character used a hex escape 59 // sequence, do not emit 0-9 and a-f as-is. C++ interprets "\x123" 60 // as a single (overflowing) escape sequence, rather than '\x12' 61 // followed by '3'. 62 isHexDigit := ('0' <= b && b <= '9') || ('a' <= b && b <= 'f') || ('A' <= b && b <= 'F') 63 if 32 <= b && b <= 126 && !(lastWasHex && isHexDigit) { 64 buf.WriteByte(b) 65 } else { 66 fmt.Fprintf(&buf, "\\x%02x", b) 67 wasHex = true 68 } 69 } 70 lastWasHex = wasHex 71 } 72 buf.WriteByte('"') 73 return buf.String() 74} 75 76func main() { 77 flag.Parse() 78 79 var files []string 80 if len(*fileList) != 0 { 81 data, err := os.ReadFile(*fileList) 82 if err != nil { 83 fmt.Fprintf(os.Stderr, "Error reading %s: %s.\n", *fileList, err) 84 os.Exit(1) 85 } 86 files = strings.FieldsFunc(string(data), func(r rune) bool { return r == '\r' || r == '\n' }) 87 } 88 89 files = append(files, flag.Args()...) 90 91 fmt.Printf(`/* Copyright (c) 2017, Google Inc. 92 * 93 * Permission to use, copy, modify, and/or distribute this software for any 94 * purpose with or without fee is hereby granted, provided that the above 95 * copyright notice and this permission notice appear in all copies. 96 * 97 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 99 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 100 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 101 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 102 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 103 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 104 105/* This file is generated by: 106`) 107 fmt.Printf(" * go run util/embed_test_data.go") 108 for _, arg := range files { 109 fmt.Printf(" \\\n * %s", arg) 110 } 111 fmt.Printf(" */\n") 112 113 fmt.Printf(` 114/* clang-format off */ 115 116#include <stdlib.h> 117#include <string.h> 118 119#include <algorithm> 120#include <string> 121 122 123`) 124 125 // MSVC limits the length of string constants, so we emit an array of 126 // them and concatenate at runtime. We could also use a single array 127 // literal, but this is less compact. 128 const chunkSize = 8192 129 130 for i, arg := range files { 131 data, err := os.ReadFile(arg) 132 if err != nil { 133 fmt.Fprintf(os.Stderr, "Error reading %s: %s.\n", arg, err) 134 os.Exit(1) 135 } 136 fmt.Printf("static const size_t kLen%d = %d;\n\n", i, len(data)) 137 138 fmt.Printf("static const char *kData%d[] = {\n", i) 139 for len(data) > 0 { 140 chunk := chunkSize 141 if chunk > len(data) { 142 chunk = len(data) 143 } 144 fmt.Printf(" %s,\n", quote(data[:chunk])) 145 data = data[chunk:] 146 } 147 fmt.Printf("};\n") 148 } 149 150 fmt.Printf(`static std::string AssembleString(const char **data, size_t len) { 151 std::string ret; 152 for (size_t i = 0; i < len; i += %d) { 153 size_t chunk = std::min(static_cast<size_t>(%d), len - i); 154 ret.append(data[i / %d], chunk); 155 } 156 return ret; 157} 158 159/* Silence -Wmissing-declarations. */ 160std::string GetTestData(const char *path); 161 162std::string GetTestData(const char *path) { 163`, chunkSize, chunkSize, chunkSize) 164 for i, arg := range files { 165 fmt.Printf(" if (strcmp(path, %s) == 0) {\n", quote([]byte(arg))) 166 fmt.Printf(" return AssembleString(kData%d, kLen%d);\n", i, i) 167 fmt.Printf(" }\n") 168 } 169 fmt.Printf(` fprintf(stderr, "File not embedded: %%s.\n", path); 170 abort(); 171} 172`) 173 174} 175