Mercurial > libedl
view src/edl.c @ 13:41b74137e201
include: make header guards sane
double underscores are reserved to the implementation by
the C standard
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Wed, 03 Apr 2024 20:48:51 -0400 |
parents | 0cc2555db371 |
children | 2d7c810a1ac2 |
line wrap: on
line source
#include <string.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include "edl.h" #include "str.h" #include "datatypes.h" #include "util.h" /* CRC32 hashes of EDL headers */ #define EDL_HEADER_ID 0x1541c503 #define EDL_HEADER_TRACK 0x4b211812 #define EDL_HEADER_STARTTIME 0xbb46516f #define EDL_HEADER_LENGTH 0xaeac5df7 #define EDL_HEADER_PLAYRATE 0x03834606 #define EDL_HEADER_LOCKED 0x0c2083d3 #define EDL_HEADER_NORMALIZED 0xe60c8b1d #define EDL_HEADER_STRETCHMETHOD 0xbe5802c9 #define EDL_HEADER_LOOPED 0x4ec8be4c #define EDL_HEADER_ONRULER 0xe6cb84d1 #define EDL_HEADER_MEDIATYPE 0x035f84c2 #define EDL_HEADER_FILENAME 0x379d32c5 #define EDL_HEADER_STREAM 0x9f334738 #define EDL_HEADER_STREAMSTART 0x07b4e0e7 #define EDL_HEADER_STREAMLENGTH 0x8f16c7b8 #define EDL_HEADER_FADETIMEIN 0xd2edd7e4 #define EDL_HEADER_FADETIMEOUT 0x792e8c40 #define EDL_HEADER_SUSTAINGAIN 0x98374657 #define EDL_HEADER_CURVEIN 0x3e998b1f #define EDL_HEADER_GAININ 0x82fb09c4 #define EDL_HEADER_CURVEOUT 0x53add388 #define EDL_HEADER_GAINOUT 0x4210ba56 #define EDL_HEADER_LAYER 0x89c4df6c #define EDL_HEADER_COLOR 0xadf2f1a3 #define EDL_HEADER_CURVEINR 0xa56b43e1 #define EDL_HEADER_CURVEOUTR 0xcb6d715e #define EDL_HEADER_PLAYPITCH 0x9da1b9ed #define EDL_HEADER_LOCKPITCH 0x2bda6ed4 #define EDL_HEADER_FIRSTCHANNEL 0x3071a4c6 #define EDL_HEADER_CHANNELS 0xe94981a4 typedef struct { uint32_t* order; size_t size; size_t capacity; } EDL_header; /* use a CRC32 hash function to process the headers */ static uint32_t EDL_internal_crcn32b(const unsigned char* message, size_t length) { uint32_t crc = 0xffffffff; size_t i; for (i = 0; i < length && message[i]; i++) { size_t j; crc = crc ^ message[i]; for (j = 0; j < 8; j++) crc = (crc >> 1) ^ (0xedb88320 & -(crc & 1)); } return ~crc; } static size_t EDL_internal_size_t_min(size_t a, size_t b) { return (a < b) ? a : b; } static int EDL_internal_header_reallocate(EDL_header* header, size_t new_capacity) { header->order = realloc(header->order, new_capacity * sizeof(*header->order)); if (!header->order) return -1; if (new_capacity > header->capacity) memset(&header->order[header->capacity], 0, (new_capacity - header->capacity) * sizeof(*header->order)); header->capacity = new_capacity; return header->capacity; } static int EDL_internal_parse_header_item(EDL_header* header, const char* data, size_t offset, size_t length) { if (header->capacity <= header->size) if (EDL_internal_header_reallocate(header, header->capacity * 2) < 0) return -1; header->order[header->size++] = EDL_internal_crcn32b((const unsigned char*)&data[offset], length); return header->size; } /* EDL_header MUST be zero-initialized. */ static int EDL_internal_parse_header(EDL_header* header, const char* data, size_t offset, size_t length) { size_t newline = 0, overall = 0, column = 0; { const char* ln = memchr(&data[offset], '\n', length - offset); if (!ln) return -1; newline = ln ? (ln - &data[offset]) : length; } EDL_internal_header_reallocate(header, 32); for (; overall <= newline; overall++) { if (data[offset + overall] == ';' || data[offset + overall] == ':') { if (EDL_internal_parse_header_item(header, data, overall - column, column) < 0) return -1; column = 0; /* reset */ } else column++; } return overall; } static int EDL_internal_free_header(EDL_header* header) { free(header->order); } int EDL_reallocate(EDL* edl, size_t new_capacity) { edl->arr = realloc(edl->arr, new_capacity * sizeof(EDL_line)); if (!edl->arr) return -1; if (new_capacity > edl->capacity) memset(&edl->arr[edl->capacity], 0, (new_capacity - edl->capacity) * sizeof(EDL_line)); edl->capacity = new_capacity; return edl->capacity; } int EDL_append(EDL* edl, EDL_line line) { if (edl->size + 1 < edl->capacity && !EDL_reallocate(edl, edl->capacity)) return 0; edl->arr[++edl->size] = line; return 1; } /* the big important function */ EDL_parse_error_t EDL_parse(EDL* edl, const char* data, size_t length) { size_t offset = 0; EDL_header header = {0}; if (EDL_reallocate(edl, 16) < 0) return EDL_PARSE_ERROR_OUT_OF_MEMORY; if (EDL_internal_parse_header(&header, data, offset, length) < 0) return EDL_PARSE_ERROR_HEADER; while ((offset = (const char*)memchr(&data[offset], '\n', length - offset) - data + 1) < length) { size_t local_offset = offset; /* original offset stays intact */ bool ok = true; int i; for (i = 0; i < header.size; i++) { #define ADD_TO_OFFSET(x, a) \ { \ size_t o = a(data, local_offset, length, &edl->arr[edl->size].x); \ if (!o) \ ok = false; \ local_offset += o; \ } switch (header.order[i]) { case EDL_HEADER_ID: ADD_TO_OFFSET(id, EDL_internal_parse_int); break; case EDL_HEADER_TRACK: ADD_TO_OFFSET(track, EDL_internal_parse_int); break; case EDL_HEADER_STARTTIME: ADD_TO_OFFSET(start_time, EDL_internal_parse_double); break; case EDL_HEADER_LENGTH: ADD_TO_OFFSET(length, EDL_internal_parse_double); break; case EDL_HEADER_PLAYRATE: ADD_TO_OFFSET(play_rate, EDL_internal_parse_double); break; case EDL_HEADER_LOCKED: ADD_TO_OFFSET(locked, EDL_internal_parse_bool); break; case EDL_HEADER_NORMALIZED: ADD_TO_OFFSET(normalized, EDL_internal_parse_bool); break; case EDL_HEADER_STRETCHMETHOD: ADD_TO_OFFSET(stretch_method, EDL_internal_parse_int); break; case EDL_HEADER_LOOPED: ADD_TO_OFFSET(looped, EDL_internal_parse_bool); break; case EDL_HEADER_ONRULER: ADD_TO_OFFSET(on_ruler, EDL_internal_parse_bool); break; case EDL_HEADER_MEDIATYPE: ADD_TO_OFFSET(media_type, EDL_internal_parse_media_type); break; case EDL_HEADER_FILENAME: ADD_TO_OFFSET(filename, EDL_internal_parse_string); break; case EDL_HEADER_STREAM: ADD_TO_OFFSET(stream, EDL_internal_parse_int); break; case EDL_HEADER_STREAMSTART: ADD_TO_OFFSET(stream_start, EDL_internal_parse_double); break; case EDL_HEADER_STREAMLENGTH: ADD_TO_OFFSET(stream_length, EDL_internal_parse_double); break; case EDL_HEADER_FADETIMEIN: ADD_TO_OFFSET(fade_time_in, EDL_internal_parse_double); break; case EDL_HEADER_FADETIMEOUT: ADD_TO_OFFSET(fade_time_out, EDL_internal_parse_double); break; case EDL_HEADER_SUSTAINGAIN: ADD_TO_OFFSET(sustain_gain, EDL_internal_parse_double); break; case EDL_HEADER_CURVEIN: ADD_TO_OFFSET(curve_in, EDL_internal_parse_int); break; case EDL_HEADER_GAININ: ADD_TO_OFFSET(gain_in, EDL_internal_parse_double); break; case EDL_HEADER_CURVEOUT: ADD_TO_OFFSET(curve_out, EDL_internal_parse_int); break; case EDL_HEADER_GAINOUT: ADD_TO_OFFSET(gain_out, EDL_internal_parse_double); break; case EDL_HEADER_LAYER: ADD_TO_OFFSET(layer, EDL_internal_parse_int); break; case EDL_HEADER_COLOR: ADD_TO_OFFSET(color, EDL_internal_parse_int); break; case EDL_HEADER_CURVEINR: ADD_TO_OFFSET(curve_in_r, EDL_internal_parse_int); break; case EDL_HEADER_CURVEOUTR: ADD_TO_OFFSET(curve_out_r, EDL_internal_parse_int); break; case EDL_HEADER_PLAYPITCH: ADD_TO_OFFSET(play_pitch, EDL_internal_parse_double); break; case EDL_HEADER_LOCKPITCH: ADD_TO_OFFSET(lock_pitch, EDL_internal_parse_bool); break; case EDL_HEADER_FIRSTCHANNEL: ADD_TO_OFFSET(first_channel, EDL_internal_parse_int); break; case EDL_HEADER_CHANNELS: ADD_TO_OFFSET(channels, EDL_internal_parse_int); break; default: /* ... what */ break; #undef ADD_TO_OFFSET } } if (!ok) break; if (++edl->size >= edl->capacity) if (EDL_reallocate(edl, edl->capacity * 2) < 0) return EDL_PARSE_ERROR_OUT_OF_MEMORY; } EDL_internal_free_header(&header); /* put on the shrinkwrap */ if (EDL_reallocate(edl, edl->size) < 0) return EDL_PARSE_ERROR_OUT_OF_MEMORY; return EDL_PARSE_ERROR_SUCCESS; } static void EDL_dump_line(EDL_internal_string* str, const EDL_line* line, const EDL_header* header) { size_t i; for (i = 0; i < header->size; i++) { switch (header->order[i]) { case EDL_HEADER_ID: EDL_internal_append_integer_to_string(str, line->id); break; case EDL_HEADER_TRACK: EDL_internal_append_integer_to_string(str, line->track); break; case EDL_HEADER_STARTTIME: EDL_internal_append_double_to_string(str, line->start_time); break; case EDL_HEADER_LENGTH: EDL_internal_append_double_to_string(str, line->length); break; case EDL_HEADER_PLAYRATE: EDL_internal_append_double_to_string(str, line->play_rate); break; case EDL_HEADER_LOCKED: EDL_internal_append_bool_to_string(str, line->locked); break; case EDL_HEADER_NORMALIZED: EDL_internal_append_bool_to_string(str, line->normalized); break; case EDL_HEADER_STRETCHMETHOD: EDL_internal_append_integer_to_string(str, line->stretch_method); break; case EDL_HEADER_LOOPED: EDL_internal_append_bool_to_string(str, line->looped); break; case EDL_HEADER_ONRULER: EDL_internal_append_bool_to_string(str, line->on_ruler); break; case EDL_HEADER_MEDIATYPE: EDL_internal_append_media_type_to_string(str, line->media_type); break; case EDL_HEADER_FILENAME: EDL_internal_string_append(str, "\"", 1); EDL_internal_string_append(str, line->filename, strlen(line->filename)); EDL_internal_string_append(str, "\"", 1); break; case EDL_HEADER_STREAM: EDL_internal_append_integer_to_string(str, line->stream); break; case EDL_HEADER_STREAMSTART: EDL_internal_append_double_to_string(str, line->stream_start); break; case EDL_HEADER_STREAMLENGTH: EDL_internal_append_double_to_string(str, line->stream_length); break; case EDL_HEADER_FADETIMEIN: EDL_internal_append_double_to_string(str, line->fade_time_in); break; case EDL_HEADER_FADETIMEOUT: EDL_internal_append_double_to_string(str, line->fade_time_out); break; case EDL_HEADER_SUSTAINGAIN: EDL_internal_append_double_to_string(str, line->sustain_gain); break; case EDL_HEADER_CURVEIN: EDL_internal_append_integer_to_string(str, line->curve_in); break; case EDL_HEADER_GAININ: EDL_internal_append_double_to_string(str, line->gain_in); break; case EDL_HEADER_CURVEOUT: EDL_internal_append_integer_to_string(str, line->curve_out); break; case EDL_HEADER_GAINOUT: EDL_internal_append_double_to_string(str, line->gain_out); break; case EDL_HEADER_LAYER: EDL_internal_append_integer_to_string(str, line->layer); break; case EDL_HEADER_COLOR: EDL_internal_append_integer_to_string(str, line->color); break; case EDL_HEADER_CURVEINR: EDL_internal_append_integer_to_string(str, line->curve_in_r); break; case EDL_HEADER_CURVEOUTR: EDL_internal_append_integer_to_string(str, line->curve_out_r); break; case EDL_HEADER_PLAYPITCH: EDL_internal_append_double_to_string(str, line->play_pitch); break; case EDL_HEADER_LOCKPITCH: EDL_internal_append_bool_to_string(str, line->lock_pitch); break; case EDL_HEADER_FIRSTCHANNEL: EDL_internal_append_integer_to_string(str, line->first_channel); break; case EDL_HEADER_CHANNELS: EDL_internal_append_integer_to_string(str, line->channels); break; default: /* ... what */ break; } if (i < header->size - 1) EDL_internal_string_append(str, ";\t", 2); else EDL_internal_string_append(str, ";", 1); } EDL_internal_string_append(str, "\n", 1); } /* this gets progressively slower and slower... oops */ char* EDL_dump(const EDL* edl) { static const char order_str[] = "\"ID\";\"Track\";\"StartTime\";\"Length\";\"PlayRate\";\"Locked\";\"Normalized\";\"StretchMethod\";" "\"Looped\";\"OnRuler\";\"MediaType\";\"FileName\";\"Stream\";\"StreamStart\";\"StreamLength\";" "\"FadeTimeIn\";\"FadeTimeOut\";\"SustainGain\";\"CurveIn\";\"GainIn\";\"CurveOut\";\"GainOut\";" "\"Layer\";\"Color\";\"CurveInR\";\"CurveOutR\":\"PlayPitch\";\"LockPitch\";\"FirstChannel\";\"Channels\"\n"; EDL_internal_string ret; EDL_internal_string_init(&ret); EDL_internal_string_append(&ret, order_str, strlen(order_str)); { EDL_header header = {0}; if (EDL_internal_parse_header(&header, order_str, 0, strlen(order_str)) < 0) return NULL; /* how? */ size_t i; for (i = 0; i < edl->size; i++) EDL_dump_line(&ret, &edl->arr[i], &header); EDL_internal_free_header(&header); } return ret.data; } void EDL_free(EDL* edl) { size_t i; for (i = 0; i < edl->size; i++) { if (edl->arr[i].filename) free(edl->arr[i].filename); } free(edl->arr); }