bool ebml_write_header(struct ebml_writer *writer, const_bstring format_name) { if (!ebml_start_tag(writer, EBML_HEADER_TAG)) return false; bstring name_buf = bfromcstralloc(128, ""); if (!name_buf) return false; bool ok = true; if (binsertch(name_buf, 0, 32, '\0') != BSTR_OK) { fprintf(stderr, "bpattern()\n"); ok = false; goto out; } if (bsetstr(name_buf, 0, format_name, '\0') != BSTR_OK) { fprintf(stderr, "bsetstr()\n"); ok = false; goto out; } if (!fwrite(name_buf->data, name_buf->slen + 1, 1, writer->f)) { perror("fwrite"); ok = false; goto out; } ebml_end_tag(writer); out: bdestroy(name_buf); return ok; }
/* Description: Create a packet for sending data. * Author: Albin Severinson * Date: 07/03/15 */ bstring create_data_frame(bstring payload) { int rc = 0; unsigned char payload_length = blength(payload); assert(payload_length <= PACKET_DATA_SIZE && "Payload exceeded max size."); //Prepare packet bstring packet = bfromcstr(""); //If it's a DATA frame, add the preable if(payload_length != 0){ rc = binsertch(packet, 0, 1, preamble); check(rc == BSTR_OK, "Failed to insert preamble."); //Insert payload length rc = binsertch(packet, 1, 1, payload_length); check(rc == 0, "Failed to add package size."); debug("Preamble added"); } else{ //Insert payload length rc = binsertch(packet, 0, 1, payload_length); check(rc == 0, "Failed to add package size."); } //Concat with payload bconcat(packet, payload); //Cleanup payload bdestroy(payload); return packet; error: bdestroy(packet); return NULL; }
static inline bstring json_escape(bstring in) { if(in == NULL) return NULL; // Slightly better than the old solution. bstring vstr = bstrcpy(in); int i; for(i = 0; i < blength(vstr); i++) { if(bdata(vstr)[i] == '\\') { binsertch(vstr, i, 1, '\\'); i++; } else if(bdata(vstr)[i] == '"') { binsertch(vstr, i, 1, '\\'); i++; } } return vstr; }
/* Description: Wait for a packet from LINK and return it. * Author: Albin Severinson * Date: 03/03/15 */ bstring get_packet() { int rc = 0; int i = 0; unsigned char data_length = 0; char byte = 0; //Prepare an empty packet bstring packet = bfromcstralloc(total_packet_size, ""); //Get packet data_length = get_byte(); //rc = binsertch(packet, 0, 1, data_length); //check(rc == BSTR_OK, "Failed to add packet length."); debug("Got packet size: %d", data_length); //Get rest of packet for(i = 0;i < data_length;i++){ byte = get_byte(); rc = binsertch(packet, i, 1, byte); check(rc == BSTR_OK, "Failed to get packet."); //debug("Got [%d = %d]", i, byte); } //Send ACK frame if(data_length != 0){ rc = send_ack(); check(rc == 0, "Failed to send ACK frame."); } debug("[GOT PACKET]: %s", bdata(packet)); return packet; error: bdestroy(packet); return NULL; }
const char* glswGetShader(const char* pEffectKey, const char* pContentsFromMemory) { glswContext* gc = __glsw__Context; bstring effectKey; glswList* closestMatch = 0; struct bstrList* tokens; bstring effectName; glswList* pLoadedEffect; glswList* pShaderEntry; bstring shaderKey = 0; if (!gc) { return 0; } // Extract the effect name from the effect key effectKey = bfromcstr(pEffectKey); tokens = bsplit(effectKey, '.'); if (!tokens || !tokens->qty) { bdestroy(gc->ErrorMessage); gc->ErrorMessage = bformat("Malformed effect key key '%s'.", pEffectKey); bstrListDestroy(tokens); bdestroy(effectKey); return 0; } effectName = tokens->entry[0]; // Check if we already loaded this effect file pLoadedEffect = gc->LoadedEffects; while (pLoadedEffect) { if (1 == biseq(pLoadedEffect->Key, effectName)) { break; } pLoadedEffect = pLoadedEffect->Next; } // If we haven't loaded this file yet, load it in if (!pLoadedEffect) { bstring effectContents; struct bstrList* lines; int lineNo; if (!pContentsFromMemory) { FILE* fp; bstring effectFile; // Decorate the effect name to form the fullpath effectFile = bstrcpy(effectName); binsert(effectFile, 0, gc->PathPrefix, '?'); bconcat(effectFile, gc->PathSuffix); // Attempt to open the file fp = fopen((const char*) effectFile->data, "rb"); if (!fp) { bdestroy(gc->ErrorMessage); gc->ErrorMessage = bformat("Unable to open effect file '%s'.", effectFile->data); bdestroy(effectFile); bdestroy(effectKey); bstrListDestroy(tokens); return 0; } // Add a new entry to the front of gc->LoadedEffects { glswList* temp = gc->LoadedEffects; gc->LoadedEffects = (glswList*) calloc(sizeof(glswList), 1); gc->LoadedEffects->Key = bstrcpy(effectName); gc->LoadedEffects->Next = temp; } // Read in the effect file effectContents = bread((bNread) fread, fp); fclose(fp); bdestroy(effectFile); } else { effectContents = bfromcstr(pContentsFromMemory); } lines = bsplit(effectContents, '\n'); bdestroy(effectContents); effectContents = 0; for (lineNo = 0; lineNo < lines->qty; lineNo++) { bstring line = lines->entry[lineNo]; // If the line starts with "--", then it marks a new section if (blength(line) >= 2 && line->data[0] == '-' && line->data[1] == '-') { // Find the first character in [A-Za-z0-9_]. int colNo; for (colNo = 2; colNo < blength(line); colNo++) { char c = line->data[colNo]; if (__glsw__Alphanumeric(c)) { break; } } // If there's no alphanumeric character, // then this marks the start of a new comment block. if (colNo >= blength(line)) { bdestroy(shaderKey); shaderKey = 0; } else { // Keep reading until a non-alphanumeric character is found. int endCol; for (endCol = colNo; endCol < blength(line); endCol++) { char c = line->data[endCol]; if (!__glsw__Alphanumeric(c)) { break; } } bdestroy(shaderKey); shaderKey = bmidstr(line, colNo, endCol - colNo); // Add a new entry to the shader map. { glswList* temp = gc->ShaderMap; gc->ShaderMap = (glswList*) calloc(sizeof(glswList), 1); gc->ShaderMap->Key = bstrcpy(shaderKey); gc->ShaderMap->Next = temp; gc->ShaderMap->Value = bformat("#line %d\n", lineNo); binsertch(gc->ShaderMap->Key, 0, 1, '.'); binsert(gc->ShaderMap->Key, 0, effectName, '?'); } // Check for a version mapping. if (gc->TokenMap) { struct bstrList* tokens = bsplit(shaderKey, '.'); glswList* pTokenMapping = gc->TokenMap; while (pTokenMapping) { bstring directive = 0; int tokenIndex; // An empty key in the token mapping means "always prepend this directive". // The effect name itself is also checked against the token mapping. if (0 == blength(pTokenMapping->Key) || 1 == biseq(pTokenMapping->Key, effectName)) { directive = pTokenMapping->Value; binsert(gc->ShaderMap->Value, 0, directive, '?'); } // Check all tokens in the current section divider for a mapped token. for (tokenIndex = 0; tokenIndex < tokens->qty && !directive; tokenIndex++) { bstring token = tokens->entry[tokenIndex]; if (1 == biseq(pTokenMapping->Key, token)) { directive = pTokenMapping->Value; binsert(gc->ShaderMap->Value, 0, directive, '?'); } } pTokenMapping = pTokenMapping->Next; } bstrListDestroy(tokens); } } continue; } if (shaderKey) { bconcat(gc->ShaderMap->Value, line); bconchar(gc->ShaderMap->Value, '\n'); } } // Cleanup bstrListDestroy(lines); bdestroy(shaderKey); } // Find the longest matching shader key pShaderEntry = gc->ShaderMap; while (pShaderEntry) { if (binstr(effectKey, 0, pShaderEntry->Key) == 0 && (!closestMatch || blength(pShaderEntry->Key) > blength(closestMatch->Key))) { closestMatch = pShaderEntry; } pShaderEntry = pShaderEntry->Next; } bstrListDestroy(tokens); bdestroy(effectKey); if (!closestMatch) { bdestroy(gc->ErrorMessage); gc->ErrorMessage = bformat("Could not find shader with key '%s'.", pEffectKey); return 0; } return (const char*) closestMatch->Value->data; }
int main(int argc, char** argv) { uint64_t iter = 100; uint32_t i; uint32_t j; int globalNumberOfThreads = 0; int optPrintDomains = 0; int c; ThreadUserData myData; bstring testcase = bfromcstr("none"); uint64_t numberOfWorkgroups = 0; int tmp = 0; double time; double cycPerUp = 0.0; const TestCase* test = NULL; uint64_t realSize = 0; uint64_t realIter = 0; uint64_t maxCycles = 0; uint64_t minCycles = UINT64_MAX; uint64_t cyclesClock = 0; uint64_t demandIter = 0; TimerData itertime; Workgroup* currentWorkgroup = NULL; Workgroup* groups = NULL; uint32_t min_runtime = 1; /* 1s */ bstring HLINE = bfromcstr(""); binsertch(HLINE, 0, 80, '-'); binsertch(HLINE, 80, 1, '\n'); int (*ownprintf)(const char *format, ...); ownprintf = &printf; /* Handling of command line options */ if (argc == 1) { HELP_MSG; exit(EXIT_SUCCESS); } while ((c = getopt (argc, argv, "w:t:s:l:aphvi:")) != -1) { switch (c) { case 'h': HELP_MSG; exit (EXIT_SUCCESS); case 'v': VERSION_MSG; exit (EXIT_SUCCESS); case 'a': ownprintf(TESTS"\n"); exit (EXIT_SUCCESS); case 'w': numberOfWorkgroups++; break; case 's': min_runtime = atoi(optarg); break; case 'i': demandIter = strtoul(optarg, NULL, 10); if (demandIter <= 0) { fprintf (stderr, "Error: Iterations must be greater than 0\n"); return EXIT_FAILURE; } break; case 'l': bdestroy(testcase); testcase = bfromcstr(optarg); for (i=0; i<NUMKERNELS; i++) { if (biseqcstr(testcase, kernels[i].name)) { test = kernels+i; break; } } if (test == NULL) { fprintf (stderr, "Error: Unknown test case %s\n",optarg); return EXIT_FAILURE; } else { ownprintf("Name: %s\n",test->name); ownprintf("Number of streams: %d\n",test->streams); ownprintf("Loop stride: %d\n",test->stride); ownprintf("Flops: %d\n",test->flops); ownprintf("Bytes: %d\n",test->bytes); switch (test->type) { case INT: ownprintf("Data Type: Integer\n"); break; case SINGLE: ownprintf("Data Type: Single precision float\n"); break; case DOUBLE: ownprintf("Data Type: Double precision float\n"); break; } if (test->loads >= 0) { ownprintf("Load Ops: %d\n",test->loads); } if (test->stores >= 0) { ownprintf("Store Ops: %d\n",test->stores); } if (test->branches >= 0) { ownprintf("Branches: %d\n",test->branches); } if (test->instr_const >= 0) { ownprintf("Constant instructions: %d\n",test->instr_const); } if (test->instr_loop >= 0) { ownprintf("Loop instructions: %d\n",test->instr_loop); } } bdestroy(testcase); exit (EXIT_SUCCESS); break; case 'p': optPrintDomains = 1; break; case 'g': numberOfWorkgroups = LLU_CAST atol(optarg); tmp = numberOfWorkgroups; break; case 't': bdestroy(testcase); testcase = bfromcstr(optarg); for (i=0; i<NUMKERNELS; i++) { if (biseqcstr(testcase, kernels[i].name)) { test = kernels+i; break; } } if (test == NULL) { fprintf (stderr, "Error: Unknown test case %s\n",optarg); return EXIT_FAILURE; } bdestroy(testcase); break; case '?': if (isprint (optopt)) fprintf (stderr, "Unknown option `-%c'.\n", optopt); else fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); return EXIT_FAILURE; default: HELP_MSG; } } if ((numberOfWorkgroups == 0) && (!optPrintDomains)) { fprintf(stderr, "Error: At least one workgroup (-w) must be set on commandline\n"); exit (EXIT_FAILURE); } if (topology_init() != EXIT_SUCCESS) { fprintf(stderr, "Error: Unsupported processor!\n"); exit(EXIT_FAILURE); } if ((test == NULL) && (!optPrintDomains)) { fprintf(stderr, "Unknown test case. Please check likwid-bench -a for available tests\n"); fprintf(stderr, "and select one using the -t commandline option\n"); exit(EXIT_FAILURE); } numa_init(); affinity_init(); timer_init(); if (optPrintDomains) { bdestroy(testcase); AffinityDomains_t affinity = get_affinityDomains(); ownprintf("Number of Domains %d\n",affinity->numberOfAffinityDomains); for (i=0; i < affinity->numberOfAffinityDomains; i++ ) { ownprintf("Domain %d:\n",i); ownprintf("\tTag %s:",bdata(affinity->domains[i].tag)); for ( uint32_t j=0; j < affinity->domains[i].numberOfProcessors; j++ ) { ownprintf(" %d",affinity->domains[i].processorList[j]); } ownprintf("\n"); } exit (EXIT_SUCCESS); } allocator_init(numberOfWorkgroups * MAX_STREAMS); groups = (Workgroup*) malloc(numberOfWorkgroups*sizeof(Workgroup)); tmp = 0; optind = 0; while ((c = getopt (argc, argv, "w:t:s:l:i:aphv")) != -1) { switch (c) { case 'w': currentWorkgroup = groups+tmp; bstring groupstr = bfromcstr(optarg); i = bstr_to_workgroup(currentWorkgroup, groupstr, test->type, test->streams); bdestroy(groupstr); if (i == 0) { for (i=0; i< test->streams; i++) { if (currentWorkgroup->streams[i].offset%test->stride) { fprintf (stderr, "Error: Stream %d: offset is not a multiple of stride!\n",i); return EXIT_FAILURE; } allocator_allocateVector(&(currentWorkgroup->streams[i].ptr), PAGE_ALIGNMENT, currentWorkgroup->size, currentWorkgroup->streams[i].offset, test->type, currentWorkgroup->streams[i].domain); } tmp++; } else { exit(EXIT_FAILURE); } break; default: continue; break; } } /* :WARNING:05/04/2010 08:58:05 AM:jt: At the moment the thread * module only allows equally sized thread groups*/ for (i=0; i<numberOfWorkgroups; i++) { globalNumberOfThreads += groups[i].numberOfThreads; } ownprintf(bdata(HLINE)); ownprintf("LIKWID MICRO BENCHMARK\n"); ownprintf("Test: %s\n",test->name); ownprintf(bdata(HLINE)); ownprintf("Using %" PRIu64 " work groups\n",numberOfWorkgroups); ownprintf("Using %d threads\n",globalNumberOfThreads); ownprintf(bdata(HLINE)); threads_init(globalNumberOfThreads); threads_createGroups(numberOfWorkgroups); /* we configure global barriers only */ barrier_init(1); barrier_registerGroup(globalNumberOfThreads); cyclesClock = timer_getCycleClock(); #ifdef LIKWID_PERFMON if (getenv("LIKWID_FILEPATH") != NULL) { ownprintf("Using Likwid Marker API\n"); } LIKWID_MARKER_INIT; ownprintf(bdata(HLINE)); #endif /* initialize data structures for threads */ for (i=0; i<numberOfWorkgroups; i++) { myData.iter = iter; if (demandIter > 0) { myData.iter = demandIter; } myData.min_runtime = min_runtime; myData.size = groups[i].size; myData.test = test; myData.cycles = 0; myData.numberOfThreads = groups[i].numberOfThreads; myData.processors = (int*) malloc(myData.numberOfThreads * sizeof(int)); myData.streams = (void**) malloc(test->streams * sizeof(void*)); for (j=0; j<groups[i].numberOfThreads; j++) { myData.processors[j] = groups[i].processorIds[j]; } for (j=0; j< test->streams; j++) { myData.streams[j] = groups[i].streams[j].ptr; } threads_registerDataGroup(i, &myData, copyThreadData); free(myData.processors); free(myData.streams); } if (demandIter == 0) { getIterSingle((void*) &threads_data[0]); for (i=0; i<numberOfWorkgroups; i++) { iter = threads_updateIterations(i, demandIter); } } #ifdef DEBUG_LIKWID else { ownprintf("Using manually selected iterations per thread\n"); } #endif threads_create(runTest); threads_join(); for (int i=0; i<globalNumberOfThreads; i++) { realSize += threads_data[i].data.size; realIter += threads_data[i].data.iter; if (threads_data[i].cycles > maxCycles) { maxCycles = threads_data[i].cycles; } if (threads_data[i].cycles < minCycles) { minCycles = threads_data[i].cycles; } } time = (double) maxCycles / (double) cyclesClock; ownprintf(bdata(HLINE)); ownprintf("Cycles:\t\t\t%" PRIu64 "\n", maxCycles); ownprintf("CPU Clock:\t\t%" PRIu64 "\n", timer_getCpuClock()); ownprintf("Cycle Clock:\t\t%" PRIu64 "\n", cyclesClock); ownprintf("Time:\t\t\t%e sec\n", time); ownprintf("Iterations:\t\t%" PRIu64 "\n", realIter); ownprintf("Iterations per thread:\t%" PRIu64 "\n",threads_data[0].data.iter); ownprintf("Inner loop executions:\t%.0f\n", ((double)realSize)/((double)test->stride)); ownprintf("Size:\t\t\t%" PRIu64 "\n", realSize*test->bytes ); ownprintf("Size per thread:\t%" PRIu64 "\n", threads_data[0].data.size*test->bytes); ownprintf("Number of Flops:\t%" PRIu64 "\n", (threads_data[0].data.iter * realSize * test->flops)); ownprintf("MFlops/s:\t\t%.2f\n", 1.0E-06 * ((double) threads_data[0].data.iter * realSize * test->flops/ time)); ownprintf("Data volume (Byte):\t%llu\n", LLU_CAST (threads_data[0].data.iter * realSize * test->bytes)); ownprintf("MByte/s:\t\t%.2f\n", 1.0E-06 * ( (double) threads_data[0].data.iter * realSize * test->bytes/ time)); cycPerUp = ((double) maxCycles / (double) (threads_data[0].data.iter * realSize)); ownprintf("Cycles per update:\t%f\n", cycPerUp); switch ( test->type ) { case INT: case SINGLE: ownprintf("Cycles per cacheline:\t%f\n", (16.0 * cycPerUp)); break; case DOUBLE: ownprintf("Cycles per cacheline:\t%f\n", (8.0 * cycPerUp)); break; } ownprintf("Loads per update:\t%ld\n", test->loads ); ownprintf("Stores per update:\t%ld\n", test->stores ); if ((test->loads > 0) && (test->stores > 0)) { ownprintf("Load/store ratio:\t%.2f\n", ((double)test->loads)/((double)test->stores) ); } if ((test->instr_loop > 0) && (test->instr_const > 0)) { ownprintf("Instructions:\t\t%" PRIu64 "\n", LLU_CAST ((double)realSize/test->stride)*test->instr_loop*threads_data[0].data.iter + test->instr_const ); } if (test->uops > 0) { ownprintf("UOPs:\t\t\t%" PRIu64 "\n", LLU_CAST ((double)realSize/test->stride)*test->uops*threads_data[0].data.iter); } ownprintf(bdata(HLINE)); threads_destroy(numberOfWorkgroups, test->streams); allocator_finalize(); workgroups_destroy(&groups, numberOfWorkgroups, test->streams); #ifdef LIKWID_PERFMON if (getenv("LIKWID_FILEPATH") != NULL) { ownprintf("Writing Likwid Marker API results to file %s\n", getenv("LIKWID_FILEPATH")); } LIKWID_MARKER_CLOSE; #endif bdestroy(HLINE); return EXIT_SUCCESS; }
int main(int argc, char* argv[]) { bstring ldargs = bfromcstr(""); int i, result; unsigned int match = 0, unmatch = 0; char ca, ce; BFILE* expect; BFILE* actual; // Define arguments. struct arg_lit* show_help = arg_lit0("h", "help", "Show this help."); struct arg_lit* gen_relocatable = arg_lit0("r", "relocatable", "Generate relocatable code."); struct arg_lit* gen_intermediate = arg_lit0("i", "intermediate", "Generate intermediate code for use with the linker."); struct arg_lit* little_endian_mode = arg_lit0(NULL, "little-endian", "Use little endian serialization."); struct arg_file* input_file = arg_file1(NULL, NULL, "<file>", "The input assembly file."); struct arg_file* expect_file = arg_file0("e", "expect", "<file>", "The output file that contains expected output."); struct arg_file* actual_file = arg_file1("a", "actual", "<file>", "The output file where actual output will be placed."); struct arg_file* symbols_file = arg_file0("s", "debug-symbols", "<file>", "The debugging symbol output file."); struct arg_lit* fail_opt = arg_lit0("f", "fail", "The assembler is expected to fail and the actual output file should not exist on completion."); struct arg_file* path = arg_file1("p", NULL, "<path>", "The path to the assembler."); struct arg_lit* verbose = arg_litn("v", NULL, 0, LEVEL_EVERYTHING - LEVEL_DEFAULT, "Increase verbosity."); struct arg_lit* quiet = arg_litn("q", NULL, 0, LEVEL_DEFAULT - LEVEL_SILENT, "Decrease verbosity."); struct arg_end* end = arg_end(20); void* argtable[] = { show_help, gen_relocatable, gen_intermediate, little_endian_mode, symbols_file, input_file, expect_file, actual_file, fail_opt, path, verbose, quiet, end }; // Parse arguments. int nerrors = arg_parse(argc, argv, argtable); version_print(bautofree(bfromcstr("Assembler Test Driver"))); if (nerrors != 0 || show_help->count != 0 || (fail_opt->count == 0 && (expect_file->count == 0 || actual_file->count == 0))) { if (show_help->count != 0 && fail_opt->count == 0 && (expect_file->count == 0 || actual_file->count == 0)) printd(LEVEL_ERROR, "error: you must provide either -f or -e and -a.\n"); if (show_help->count != 0) arg_print_errors(stderr, end, "testasm"); printd(LEVEL_DEFAULT, "syntax:\n testasm"); arg_print_syntax(stderr, argtable, "\n"); printd(LEVEL_DEFAULT, "options:\n"); arg_print_glossary(stderr, argtable, " %-25s %s\n"); arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); return 1; } // Set verbosity level. debug_setlevel(LEVEL_DEFAULT + verbose->count - quiet->count); // Set global path variable. osutil_setarg0(bautofree(bfromcstr(argv[0]))); // Generate the argument list for the assembler. ldargs = bfromcstr(path->filename[0]); binsertch(ldargs, 0, 1, '"'); bconchar(ldargs, '"'); bconchar(ldargs, ' '); // Verbosity options. if (verbose->count > 0) { bconchar(ldargs, '-'); for (i = 0; i < verbose->count; i++) bconchar(ldargs, 'v'); bconchar(ldargs, ' '); } if (quiet->count > 0) { bconchar(ldargs, '-'); for (i = 0; i < quiet->count; i++) bconchar(ldargs, 'q'); bconchar(ldargs, ' '); } // Literal options. if (gen_relocatable->count > 0) { bconchar(ldargs, '-'); for (i = 0; i < gen_relocatable->count; i++) bconchar(ldargs, 'r'); bconchar(ldargs, ' '); } if (gen_intermediate->count > 0) { bconchar(ldargs, '-'); for (i = 0; i < gen_intermediate->count; i++) bconchar(ldargs, 'i'); bconchar(ldargs, ' '); } if (little_endian_mode->count > 0) { for (i = 0; i < little_endian_mode->count; i++) bcatcstr(ldargs, "--little-endian "); } // Unlink the actual file so that if we are expecting // failure, we won't return incorrectly. unlink(actual_file->filename[0]); // Output file. bcatcstr(ldargs, "-o \""); bcatcstr(ldargs, actual_file->filename[0]); bcatcstr(ldargs, "\" "); // Input file. bcatcstr(ldargs, "\""); bcatcstr(ldargs, input_file->filename[0]); bcatcstr(ldargs, "\" "); // Windows needs the whole command wrapped in quotes and slashes to be correct. // See http://stackoverflow.com/questions/2642551/windows-c-system-call-with-spaces-in-command. #ifdef _WIN32 binsertch(ldargs, 0, 1, '"'); bconchar(ldargs, '"'); #endif // Now run the assembler! result = system(ldargs->data); if (result != 0 && fail_opt->count == 0) { // Assembler returned error exit code. printd(LEVEL_ERROR, "error: expected success but assembler returned non-zero exit code (%i).\n", result); return 1; } else if (result == 0 && fail_opt->count >= 1) { // Assembler returned zero when failure was expected. printd(LEVEL_ERROR, "error: expected failure but assembler returned zero exit code.\n"); return 1; } else if (result != 0 && fail_opt->count >= 1) { // Assembler failed and we expected it to. Return success only // if the output file does not exist. actual = bfopen(actual_file->filename[0], "rb"); if (actual != NULL) { printd(LEVEL_ERROR, "error: expected failure but actual output file exists.\n"); bfclose(actual); return 1; } return 0; } // Open expect data. expect = bfopen(expect_file->filename[0], "rb"); if (expect == NULL) { // The expect file was not provided. printd(LEVEL_ERROR, "error: path to expect file does not exist.\n"); return 1; } // Open actual data. actual = bfopen(actual_file->filename[0], "rb"); if (actual == NULL) { // The expect file was not provided. bfclose(expect); printd(LEVEL_ERROR, "error: expected data but actual output file does not exist after running assembler.\n"); return 1; } // Now compare raw bytes. while (true) { if (!bfeof(actual) && !bfeof(expect)) { ca = bfgetc(actual); ce = bfgetc(expect); if (ca == ce) match++; else { printd(LEVEL_WARNING, "warning: byte at 0x%04X is different (got 0x%02X, expected 0x%02X)!\n", bftell(actual), ca, ce); unmatch++; } } else if (!bfeof(actual)) { ca = bfgetc(actual); printd(LEVEL_ERROR, "error: actual output contained trailing byte 0x%02X.\n", (unsigned char)ca); unmatch++; } else if (!bfeof(expect)) { ce = bfgetc(expect); printd(LEVEL_ERROR, "error: expected actual output to contain 0x%02X.\n", (unsigned char)ce); unmatch++; } else break; } if (unmatch > 0) { printd(LEVEL_ERROR, "error: actual output differs from expected output in content (%f%%, %i bytes different).\n", 100.f / (unmatch + match) * unmatch, unmatch); if (bftell(actual) != bftell(expect)) printd(LEVEL_ERROR, "error: actual output differs from expected output in length (%i bytes larger).\n", bftell(actual) - bftell(expect)); bfclose(actual); bfclose(expect); return 1; } // Close files and delete actual because we have // succeeded. bfclose(actual); bfclose(expect); unlink(actual_file->filename[0]); return 0; }