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