blob: 8d89d99d4222a72c18453f7abdaac9e1fb3a0942 [file] [log] [blame]
// Copyright (c) 2015, Google Inc.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package main
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"os"
"path"
"sort"
"strconv"
)
// libraryNames must be kept in sync with the enum in err.h. The generated code
// will contain static assertions to enforce this.
var libraryNames = []string{
"NONE",
"SYS",
"BN",
"RSA",
"DH",
"EVP",
"BUF",
"OBJ",
"PEM",
"DSA",
"X509",
"ASN1",
"CONF",
"CRYPTO",
"EC",
"SSL",
"BIO",
"PKCS7",
"PKCS8",
"X509V3",
"RAND",
"ENGINE",
"OCSP",
"UI",
"COMP",
"ECDSA",
"ECDH",
"HMAC",
"DIGEST",
"CIPHER",
"HKDF",
"TRUST_TOKEN",
"USER",
}
// stringList is a map from uint32 -> string which can output data for a sorted
// list as C literals.
type stringList struct {
// entries is an array of keys and offsets into |stringData|. The
// offsets are in the bottom 15 bits of each uint32 and the key is the
// top 17 bits.
entries []uint32
// internedStrings contains the same strings as are in |stringData|,
// but allows for easy deduplication. It maps a string to its offset in
// |stringData|.
internedStrings map[string]uint32
stringData []byte
}
func newStringList() *stringList {
return &stringList{
internedStrings: make(map[string]uint32),
}
}
// offsetMask is the bottom 15 bits. It's a mask that selects the offset from a
// uint32 in entries.
const offsetMask = 0x7fff
func (st *stringList) Add(key uint32, value string) error {
if key&offsetMask != 0 {
return errors.New("need bottom 15 bits of the key for the offset")
}
offset, ok := st.internedStrings[value]
if !ok {
offset = uint32(len(st.stringData))
if offset&offsetMask != offset {
return errors.New("stringList overflow")
}
st.stringData = append(st.stringData, []byte(value)...)
st.stringData = append(st.stringData, 0)
st.internedStrings[value] = offset
}
for _, existing := range st.entries {
if existing>>15 == key>>15 {
panic("duplicate entry")
}
}
st.entries = append(st.entries, key|offset)
return nil
}
func (st *stringList) buildList() []uint32 {
sort.Slice(st.entries, func(i, j int) bool { return (st.entries[i] >> 15) < (st.entries[j] >> 15) })
return st.entries
}
type stringWriter interface {
io.Writer
WriteString(string) (int, error)
}
func (st *stringList) WriteTo(out stringWriter, name string) {
list := st.buildList()
values := "kOpenSSL" + name + "Values"
out.WriteString("const uint32_t " + values + "[] = {\n")
for _, v := range list {
fmt.Fprintf(out, " 0x%x,\n", v)
}
out.WriteString("};\n\n")
out.WriteString("const size_t " + values + "Len = sizeof(" + values + ") / sizeof(" + values + "[0]);\n\n")
stringData := "kOpenSSL" + name + "StringData"
out.WriteString("const char " + stringData + "[] =\n \"")
for i, c := range st.stringData {
if c == 0 {
out.WriteString("\\0\"\n \"")
continue
}
out.Write(st.stringData[i : i+1])
}
out.WriteString("\";\n\n")
}
type errorData struct {
reasons *stringList
libraryMap map[string]uint32
}
func (e *errorData) readErrorDataFile(filename string) error {
inFile, err := os.Open(filename)
if err != nil {
return err
}
defer inFile.Close()
scanner := bufio.NewScanner(inFile)
comma := []byte(",")
lineNo := 0
for scanner.Scan() {
lineNo++
line := scanner.Bytes()
if len(line) == 0 {
continue
}
parts := bytes.Split(line, comma)
if len(parts) != 3 {
return fmt.Errorf("bad line %d in %s: found %d values but want 3", lineNo, filename, len(parts))
}
libNum, ok := e.libraryMap[string(parts[0])]
if !ok {
return fmt.Errorf("bad line %d in %s: unknown library", lineNo, filename)
}
if libNum >= 64 {
return fmt.Errorf("bad line %d in %s: library value too large", lineNo, filename)
}
key, err := strconv.ParseUint(string(parts[1]), 10 /* base */, 32 /* bit size */)
if err != nil {
return fmt.Errorf("bad line %d in %s: %s", lineNo, filename, err)
}
if key >= 2048 {
return fmt.Errorf("bad line %d in %s: key too large", lineNo, filename)
}
value := string(parts[2])
listKey := libNum<<26 | uint32(key)<<15
err = e.reasons.Add(listKey, value)
if err != nil {
return err
}
}
return scanner.Err()
}
type ErrDataTask struct {
TargetName string
Inputs []string
}
func (t *ErrDataTask) Destination() string {
return path.Join("gen", t.TargetName, "err_data.c")
}
func (t *ErrDataTask) Run() ([]byte, error) {
e := &errorData{
reasons: newStringList(),
libraryMap: make(map[string]uint32),
}
for i, name := range libraryNames {
e.libraryMap[name] = uint32(i) + 1
}
for _, input := range t.Inputs {
if err := e.readErrorDataFile(input); err != nil {
return nil, err
}
}
var out bytes.Buffer
out.WriteString(`/* Copyright (c) 2015, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
/* This file was generated by go run ./util/pregenerate. */
#include <openssl/base.h>
#include <openssl/err.h>
#include <assert.h>
`)
for i, name := range libraryNames {
fmt.Fprintf(&out, "static_assert(ERR_LIB_%s == %d, \"library value changed\");\n", name, i+1)
}
fmt.Fprintf(&out, "static_assert(ERR_NUM_LIBS == %d, \"number of libraries changed\");\n", len(libraryNames)+1)
out.WriteString("\n")
e.reasons.WriteTo(&out, "Reason")
return out.Bytes(), nil
}