From 3aaabc63269d3e2202a66757683b22fde77a309a Mon Sep 17 00:00:00 2001 From: Julian Daube Date: Tue, 2 Jul 2019 20:05:01 +0200 Subject: [PATCH] add new test files --- .vscode/settings.json | 3 + Makefile | 14 + __pycache__/__init__.cpython-37.pyc | Bin 0 -> 4046 bytes __pycache__/import_gds.cpython-37.pyc | Bin 0 -> 3852 bytes gds/library.py | 11 + gdsimporter.code-workspace | 15 + import_gds.py | 98 +-- read.c | 863 ++++++++++++++++++++++++++ tests/test_aref.gds | Bin 0 -> 318 bytes tests/test_coords.gds | Bin 0 -> 170 bytes tests/test_sref.gds | Bin 0 -> 400 bytes 11 files changed, 968 insertions(+), 36 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 Makefile create mode 100644 __pycache__/__init__.cpython-37.pyc create mode 100644 __pycache__/import_gds.cpython-37.pyc create mode 100644 gdsimporter.code-workspace create mode 100644 read.c create mode 100644 tests/test_aref.gds create mode 100644 tests/test_coords.gds create mode 100644 tests/test_sref.gds diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b26f7c4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "/bin/python" +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3820c8e --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +OBJ:= read.o +FILE := coil.gds test_text.gds +CFLAGS += -g -Wall -Werror +CC := clang + +run: test + ./test $(FILE) + +test: $(OBJ) + $(CC) $^ -o $@ + + +clean: + $(RM) $(OBJ) test diff --git a/__pycache__/__init__.cpython-37.pyc b/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..beb026dd4e75ff062b02ddedad5aa179d6e0e91a GIT binary patch literal 4046 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 aG01!#CJu0k%fZOO$iu{^#bL // for printf +#include // for uintxx_t +#include // for assert + +#include // for perror +#include // for memcpy +#include // for malloc, free + +#define LIST_ELEMENTS(a) \ +/* file header records */ \ +a(HEADER , 0x0002) \ +a(BGNLIB , 0x0102) \ +a(LIBNAME , 0x0206) \ +a(REFLIBS , 0x1F06) \ +a(FONTS , 0x2006) \ +a(ATTRTABLE , 0x2306) \ +a(GENERATIONS , 0x2202) \ +a(FORMAT , 0x3602) \ +a(MASK , 0x3706) \ +a(ENDMASKS , 0x3800) \ +a(UNITS , 0x0305) \ +/* file tail records */ \ +a(ENDLIB , 0x0400) \ +/* structure header records */ \ +a(BGNSTR , 0x0502) \ +a(STRNAME , 0x0606) \ +a(ENDEL , 0x1100) \ +/* structure tail records */ \ +a(ENDSTR , 0x0700) \ +/* element header records */ \ +a(BOUNDARY , 0x0800) \ +a(PATH , 0x0900) \ +a(SREF , 0x0A00) \ +a(AREF , 0x0B00) \ +a(TEXT , 0x0C00) \ +a(NODE , 0x1500) \ +a(BOX , 0x2D00) \ +/* element contents records */ \ +a(ELFLAGS , 0x2601) \ +a(PLEX , 0x2F03) \ +a(LAYER , 0x0D02) \ +a(DATATYPE , 0x0E02) \ +a(XY , 0x1003) \ +a(PATHTYPE , 0x2102) \ +a(WIDTH , 0x0F03) \ +a(SNAME , 0x1206) \ +a(STRANS , 0x1A01) \ +a(MAG , 0x1B05) \ +a(ANGLE , 0x1C05) \ +a(COLROW , 0x1302) \ +a(TEXTTYPE , 0x1602) \ +a(PRESENTATION, 0x1701) \ +a(ASCII_STRING, 0x1906) \ +a(NODETYPE , 0x2A02) \ +a(BOXTYPE , 0x2E02) \ +a(STRING , 0x1906) \ +a(PROPATTR , 0x2B02) \ +a(PROPVALUE , 0x2C06) + +#define a(a,b) \ +{ \ + .name = #a, \ + .ident = b \ +}, + +#define e(a,b) \ + a = b, + +struct { + const char * name; + uint16_t ident; +} header_elements[] = { + LIST_ELEMENTS(a) +}; + +enum RECORD_IDENTIFIERS { + LIST_ELEMENTS(e) +}; + +#undef a +#undef e +#undef LIST_ELEMENTS + +typedef struct record_header { + uint16_t len; + uint16_t ident; +} record_header_t; + +typedef struct date { + uint16_t year, month, day, hour, minute, second; +} date_t; + +void swap(uint8_t * a, uint8_t * b) +{ + assert(a); + assert(b); + + uint8_t temp = *a; + *a = *b; + *b = temp; +} + +void invert(uint8_t * data, size_t s) +{ + assert(data); + + uint8_t * a = data; + uint8_t * b = data + s - 1; + + s /= 2; + + while(s--) + { + swap(a,b); + a++; + b--; + } +} + +const char * name_from_ident(uint16_t ident) +{ + for (int i = 0; i < sizeof(header_elements)/sizeof(header_elements[0]); ++i) + { + if (header_elements[i].ident == ident) + return header_elements[i].name; + } + + return NULL; +} + +typedef enum { + GSD_ELEM_UNKNOWN = 0, + GDS_ELEM_BOUNDARY, + GDS_ELEM_PATH, + GDS_ELEM_SREF, + GDS_ELEM_AREF, + GDS_ELEM_TEXT, + GDS_ELEM_NODE, + GDS_ELEM_BOX, +} gds_element_type_t; + +#define GDS_ELEM_IS_GRAPHICAL(t) ((t == GDS_ELEM_BOUNDARY) || (t == GDS_ELEM_PATH) || (t == GDS_ELEM_SREF) || (t == GDS_ELEM_AREF) || (t == GDS_ELEM_TEXT) || (t == GDS_ELEM_NODE) || (t == GDS_ELEM_BOX)) +#define GDS_ELEM_HAS_LAYER(t) ((t == GDS_ELEM_BOUNDARY) || (t == GDS_ELEM_PATH) || (t == GDS_ELEM_TEXT) || (t == GDS_ELEM_NODE) || (t == GDS_ELEM_BOX)) + +typedef struct { + gds_element_type_t type; + uint16_t elfclass; + int16_t plex; +} gds_element_base_t; + +typedef struct { + int32_t x,y; +} gds_coord_t; + +typedef struct { + gds_element_base_t base; + + size_t point_count; + gds_coord_t * points; +} gds_element_graphical_t; + +typedef struct { + gds_element_graphical_t base; + int16_t layer; + int16_t datatype; +} gds_element_boundary_t; + +typedef enum { + GDS_PATH_SQUARE = 0, + GDS_PATH_ROUNDED = 1, + GDS_PATH_SQUARE_EXTEND = 2 +} gds_path_type_t; + +typedef struct { + gds_element_graphical_t base; + int16_t layer; + int16_t datatype; + + gds_path_type_t pathtype; + + int32_t width; +} gds_element_path_t; + +typedef struct { + gds_element_graphical_t base; + + int16_t layer; + int16_t nodetype; +} gds_element_node_t; + +typedef struct { + gds_element_graphical_t base; + + int16_t layer; + int16_t texttype; + + int32_t width; + gds_path_type_t pathtype; + + uint8_t font_number:2; + + struct + { + enum + { + GDS_TEXT_TOP = 0b00, + GDS_TEXT_MIDDLE = 0b01, + GDS_TEXT_BOTTOM = 0b10, + } vertical; + + enum + { + GDS_TEXT_LEFT = 0b00, + GDS_TEXT_CENTER = 0b01, + GDS_TEXT_RIGHT = 0b10 + } horizontal; + } justification; + + char * string; +} gds_element_text_t; + +typedef union { + gds_element_type_t type; + gds_element_graphical_t graphical; + + /* element containers */ + gds_element_boundary_t boundary; + gds_element_path_t path; + gds_element_node_t node; + gds_element_text_t text; +} gds_element_t; + +typedef struct structure { + date_t creation, last_access; + char * name; + + size_t element_count; + gds_element_t * elements; +} gds_structure_t; + +typedef struct gds_file { + uint16_t version; // HEADER data + date_t last_mod, last_access; // BGNLIB data + char * libname; // LIBNAME data, must be freed + + enum { + GDS_FMT_ARCHIVED = 0, + GDS_FMT_FILTERED = 1, + } format; + + struct { + double units_per_dbunits; + double meters_per_unit; + } units; // UNITS data + + size_t structure_count; + gds_structure_t * structures; +} gds_file_t; + +/* parser errors */ +#define LIST_ERRORS(a) \ + a(GDS_NO_ERR , "Success") \ + a(GDS_ERR_MISSING_ARGS, "Missing argument(s) to record type") \ + a(GDS_ERR_NOMEM , "out of memory") \ + a(GDS_ERR_EOF , "unexpected EOF") \ + a(GDS_ERR_MISSING_HEADER , "file is missing HEADER record") \ + a(GDS_ERR_MISSING_BEGELEM , "found element records without an element start") \ + a(GDS_ERR_INVALID_FILE , "file found to be invalid") + +#define STR(a, b) #a ": " b, +#define ENUM(a, b) a, + +static const char * gds_parser_errstr[] = { LIST_ERRORS(STR) }; +typedef enum { + LIST_ERRORS(ENUM) + GDS_ERR_COUNT, +} gds_parser_err_t; + +#undef STR +#undef ENUM +#undef LIST_ERRORS + +const char * gds_strerror(gds_parser_err_t err) { + if (err >= GDS_ERR_COUNT) { + return "unknown error"; + } + + return gds_parser_errstr[err]; +} + +typedef struct parser_state { + uint8_t has_header:1; + gds_structure_t * current_structure; + gds_element_t * current_element; + + gds_parser_err_t error; + gds_file_t root; +} parser_state_t; + + +void free_gds_element(gds_element_t * this) +{ + switch(this->type) + { + default: + case GSD_ELEM_UNKNOWN: + break; + case GDS_ELEM_BOUNDARY: + case GDS_ELEM_PATH: + // clear all points + free(this->graphical.points); + this->graphical.point_count = 0; + break; + case GDS_ELEM_TEXT: + free(this->text.string); + break; + } + + // clear type + this->type = GSD_ELEM_UNKNOWN; +} + +void free_gds_structure(gds_structure_t * this) +{ + for (int i = 0; i < this->element_count; ++i) + free_gds_element(this->elements + i); + + free(this->name); + free(this->elements); +} + +void free_gds_file(gds_file_t * file) +{ + + for (int i = 0; i < file->structure_count; ++i) + free_gds_structure(file->structures + i); + + free(file->libname); + free(file->structures); +} + +// cleanup parser state from mem +void free_parser_state(parser_state_t * state) +{ + free_gds_file(&state->root); +} + +#define READ_STRUCT(file, dest) (fread((dest), sizeof(*(dest)), 1, (file))) + +int read_ushort(uint16_t * dest, FILE * file) { + if (!READ_STRUCT(file, dest)) { + return 0; + } + + invert((uint8_t*)dest, sizeof(*dest)); + return 1; +} + +int read_short(int16_t * dest, FILE * file) { + if (!READ_STRUCT(file, dest)) { + return 0; + } + + invert((uint8_t*)dest, sizeof(*dest)); + return 1; +} + +int read_int(int32_t * dest, FILE * file) { + if (!READ_STRUCT(file, dest)) + return 0; + + invert((uint8_t*)dest, sizeof(*dest)); + return 1; +} +int read_date(date_t * dest, FILE * src) { + if (!READ_STRUCT(src, dest)) { + return 0; + } + + invert((uint8_t*)&dest->day, 2); + invert((uint8_t*)&dest->month, 2); + invert((uint8_t*)&dest->year, 2); + + invert((uint8_t*)&dest->hour, 2); + invert((uint8_t*)&dest->minute, 2); + invert((uint8_t*)&dest->second, 2); + + return 1; +} + +// reads a 8 byte double value as it is represented in +// a gds file +// from spec: 1 bit sign | 7 bit exp (in 64-excess) | 7 byte mantisse +// with double = sign * 1.mantisse * 64 ** exp +int read_double(double * dest, FILE * file) { + uint8_t src[8]; + + if (!fread(src, 8, 1, file)) { + return 0; + } + + // special value, ZERO + if (*(uint64_t*)(src) == 0) { + *dest = 0; + return 1; + } + + // mantissa first + *dest = 1; + for (int i = 1; i < 8; ++i) { + *dest += ((double)src[8-i])/(1L< 0) + { + while(exp) + { + *dest *= 16.0; + exp--; + } + } + else + { + while(exp) + { + *dest /= 16.0; + exp++; + } + } + + // negation + if (src[0] & 0x80) { + *dest *= -1; + } + + return 1; +} + +int read_record_header(record_header_t * header, FILE * file) { + if (!read_ushort(&header->len, file) || + !read_ushort(&header->ident, file)) + { + return 0; + } + + header->len -= 4; + return 1; +} + +int read_coord(gds_coord_t * coord, FILE * file) +{ + if (!read_int(&coord->x, file) || !read_int(&coord->y, file)) + return 0; + + return 1; +} + +/* allocate a new element in a structure */ +gds_element_t * alloc_elem(gds_structure_t * this, gds_element_type_t type) +{ + gds_element_t * new = realloc(this->elements, (this->element_count+1)*sizeof(gds_element_t)); + + if (new == NULL) + return NULL; + + this->elements = new; + + new += this->element_count++; + + // initialize new element + *new = (gds_element_t){}; + new->type = type; + + return new; +} + +#define DATE_FMT "%2d.%02d.%04d" +#define DATE_ARGS(a) (a).day, (a).month, (a).year + +int parse_record(parser_state_t * state, FILE * file) +{ + record_header_t header = {}; + + if (!read_record_header(&header, file)) + { + perror("could not read file record"); + return 0; + } + +#define ASSERT(a) if (!(a)) do { printf("ASSERT %s:%d\n", __FILE__, __LINE__); state->error = GDS_ERR_INVALID_FILE; return 0; } while(0) + +#if 0 + printf("record %s (%04x)\n", name_from_ident(header.ident), header.ident); +#endif + + if (!state->has_header && header.ident != HEADER) + { + state->error = GDS_ERR_MISSING_HEADER; + return 0; + } + + switch (header.ident) + { + case HEADER: + ASSERT(header.len == 2); + + read_ushort(&state->root.version, file); + state->has_header = 1; + break; + + case BGNLIB: + ASSERT(header.len == 2* sizeof(date_t)); + + read_date(&state->root.last_mod , file); + read_date(&state->root.last_access, file); + + break; + + case ENDLIB: + if (state->current_structure) + printf("missing (last) ENDSTR\n"); // fixme: better warning system + + return 0; + + case LIBNAME: + state->root.libname = calloc(1, header.len + 1); + if (state->root.libname == NULL) { + state->error = GDS_ERR_NOMEM; + return 0; + } + + // copy libname to new string + if (!fread(state->root.libname, header.len, 1, file)) { + state->error = GDS_ERR_EOF; + return 0; + } + + break; + + // case REFLIBS: + // break; + // case FONTS: + // break; + // case ATTRTABLE: + // break; + // case GENERATIONS: + // break; + + case FORMAT: + ASSERT(header.len); + + uint16_t temp; + read_ushort(&temp, file); + + if (temp) + state->root.format = GDS_FMT_FILTERED; + + break; + + case UNITS: + ASSERT(header.len == 16); + + read_double(&state->root.units.units_per_dbunits, file); + read_double(&state->root.units.meters_per_unit , file); + + // now comes the first structure definition + break; + + case BGNSTR: + ASSERT(header.len == 2 * sizeof(date_t)); + + // create new structure, apppend to list + gds_structure_t * list = realloc(state->root.structures, (state->root.structure_count+1)*sizeof(gds_structure_t)); + if (!list) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + + // set new list pointer + state->root.structures = list; + state->current_structure = &state->root.structures[state->root.structure_count++]; + + // read dates + *state->current_structure = (gds_structure_t){}; + read_date(&state->current_structure->creation, file); + read_date(&state->current_structure->last_access, file); + break; + + case ENDSTR: + // done with structure + state->current_structure = NULL; + break; + + case STRNAME: + state->current_structure->name = calloc(1, header.len + 1); + + if (state->current_structure->name == NULL) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + + if (!fread(state->current_structure->name, header.len, 1, file)) + { + state->error = GDS_ERR_EOF; + return 0; + } + + break; + + + case BOUNDARY: + ASSERT(state->current_structure); + + if ((state->current_element = alloc_elem(state->current_structure, GDS_ELEM_BOUNDARY)) == NULL) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + break; + case PATH: + ASSERT(state->current_structure); + + if ((state->current_element = alloc_elem(state->current_structure, GDS_ELEM_PATH)) == NULL) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + break; + case TEXT: + ASSERT(state->current_structure); + + if ((state->current_element = alloc_elem(state->current_structure, GDS_ELEM_TEXT)) == NULL) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + break; + case NODE: + ASSERT(state->current_structure); + + if ((state->current_element = alloc_elem(state->current_structure, GDS_ELEM_NODE)) == NULL) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + break; + + case ENDEL: + state->current_element = NULL; + break; + + case DATATYPE: + if (!state->current_element) { + state->error = GDS_ERR_MISSING_BEGELEM; + return 0; + } + + switch (state->current_element->type) { + case GDS_ELEM_PATH: + read_short(&state->current_element->path.datatype, file); + break; + case GDS_ELEM_BOUNDARY: + read_short(&state->current_element->boundary.datatype, file); + break; + default: + state->error = GDS_ERR_INVALID_FILE; + return 0; + } + + break; + + case LAYER: + if (!state->current_element) + { + state->error = GDS_ERR_MISSING_BEGELEM; + return 0; + } + + ASSERT(GDS_ELEM_HAS_LAYER(state->current_element->type)); + + switch(state->current_element->type) + { + case GDS_ELEM_BOUNDARY: + read_short(&state->current_element->boundary.layer, file); + break; + case GDS_ELEM_PATH: + read_short(&state->current_element->path.layer, file); + break; + default: + // fixme: we should read a layer here, but + // its probably not implemented yet + fseek(file, header.len, SEEK_CUR); + } + break; + + case XY: + ASSERT(state->current_element); + ASSERT(GDS_ELEM_IS_GRAPHICAL(state->current_element->type)); + + if(header.len % sizeof(gds_coord_t)) + { + // alignment error + return -1; + } + + { + gds_coord_t * current = state->current_element->graphical.points = malloc(header.len); + if (!current) + { + state->error = GDS_ERR_NOMEM; + return 0; + } + + size_t size = header.len / sizeof(gds_coord_t); + state->current_element->graphical.point_count = size; + + // read all points + for(;size && read_coord(current, file);--size, ++current); + + if (size) + { + state->error = GDS_ERR_EOF; + return 0; + } + } + break; + + case WIDTH: + ASSERT(state->current_element); + + switch(state->current_element->type) { + case GDS_ELEM_PATH: + read_int(&state->current_element->path.width, file); + break; + case GDS_ELEM_TEXT: + read_int(&state->current_element->text.width, file); + default: + state->error = GDS_ERR_INVALID_FILE; + return 0; + } + + break; + case PATHTYPE: + ASSERT(state->current_element); + + { + int16_t temp; + read_short(&temp, file); + if (temp > GDS_PATH_SQUARE_EXTEND || temp < 0) + { + printf("inalid pathtype found\n"); + } + + else { + switch(state->current_element->type) { + case GDS_ELEM_PATH: + state->current_element->path.pathtype = temp; + break; + case GDS_ELEM_TEXT: + state->current_element->text.pathtype = temp; + break; + default: + printf("misplaced pathtype\n"); + break; + } + + } + + } + break; + case NODETYPE: + ASSERT(state->current_element); + ASSERT(state->current_element->type == GDS_ELEM_NODE); + + read_short(&state->current_element->node.nodetype, file); + break; + + default: + // skip record + printf("unhandled record with ident %04x (%s)\n", header.ident, name_from_ident(header.ident)); + fseek(file, header.len, SEEK_CUR); + break; + } + + return 1; +} + +gds_parser_err_t parse_gds(FILE * f, gds_file_t * file) +{ + parser_state_t state = {}; + + // go to beginning of file (just in case) + fseek(f, 0, SEEK_SET); + + // parse all records + while(!feof(f) && parse_record(&state, f)); + + // transfer ownership of file + *file = state.root; + + // reset file of parser state + state.root = (gds_file_t){}; + + // cleanup parser memory + free_parser_state(&state); + + return state.error; +} + +int main(int argc, char ** argv) +{ + printf("start\n"); + + for (char ** filename = argv + 1; filename != argv + argc; ++filename) + { + printf("reading %s...\n", *filename); + + gds_file_t file; + FILE * f = fopen(*filename, "rb"); + + if (!f) + { + perror("could not open file"); + continue; + } + + gds_parser_err_t err = parse_gds(f, &file); + + if (err != GDS_NO_ERR) { + fprintf(stderr, "error during parsing of file: %s\n", gds_strerror(err)); + } + + printf("read file\n"); + printf("last accessed: " DATE_FMT "\n", DATE_ARGS(file.last_access)); + printf("last modification: " DATE_FMT "\n", DATE_ARGS(file.last_mod)); + printf("libname: %s\n", file.libname); + + printf("units per db units: %g\n", file.units.units_per_dbunits); + printf("m/units : %g\n", file.units.meters_per_unit); + printf("file contains %ld structures\n", file.structure_count); + + for (gds_structure_t * it = file.structures; it != file.structures + file.structure_count; ++it) + { + printf("structure with name \"%s\" ", it->name); + printf("contains %ld elements\n", it->element_count); + } + + // cleanup + free_gds_file(&file); + fclose(f); + } + + printf("done\n"); + return 0; +} \ No newline at end of file diff --git a/tests/test_aref.gds b/tests/test_aref.gds new file mode 100644 index 0000000000000000000000000000000000000000..3caa67c14e5ecfa28b631197b6e7bbad917d2d98 GIT binary patch literal 318 zcmZQzV_;&6V31*CVt>rQ&cMjP%OK96g3M;%V`3{wEiQ>qEJ{sdU=U$uwR7w=_dd4# zgQd$3{ROk5LKtLNaqDJdV{>F+VBuh3VB=+CU}Rw9V`5-na1mf;U|`^7U|{(9|NlQ@ z1_nm6|Ns9@XJBCF`Tzf4J_7@55d#AQGXn!_DFXw81Oo%p3I+zoA_fM=Z!mofEP@OS zEbI(;ZRcQPE6UF=VPN5AU|rQ&cMXL!=T5YiOgo;U}E#}bYfr-VP>^+>@@d2w)}&o%MSeo zv!g;7WLWX&V`D4I&o5zM;b353<7HxCWMJcCVqjp<5n%rR|HcOxW?)bkK*lg}5Y50M O$iTqD&cMLJ!TrQ&cMVV!ob2{h|Ff-U}E#}bYfr-VP>^+>@@d2w)}&o%MSeo zv!g;7WLRhgPDx8CPGex