2 changed files with 144 additions and 0 deletions
@ -0,0 +1,143 @@ |
|||
;; VersionedData.cake: A system to read and write introspect structs with version headers |
|||
;; The intention here is to have a mostly plug-and-play system for managing e.g. user application |
|||
;; configuration files across many app versions. |
|||
(import "Introspection.cake" |
|||
"CHelpers.cake" "FileUtilities.cake") |
|||
|
|||
(c-import "<stddef.h>" "<stdio.h>" "<stdlib.h>" "<string.h>") |
|||
|
|||
(defenum versioned-data-result |
|||
versioned-data-result-success |
|||
|
|||
;; Failure codes |
|||
versioned-data-result-cannot-open-file |
|||
|
|||
versioned-data-result-version-mismatch |
|||
versioned-data-result-version-not-readable |
|||
versioned-data-result-cannot-parse-data |
|||
versioned-data-result-cannot-copy-data-into-output |
|||
|
|||
versioned-data-result-cannot-write-version |
|||
versioned-data-result-cannot-write-data) |
|||
|
|||
(def-introspect-struct version-header |
|||
version int) |
|||
|
|||
(defun-local write-version (out-file (* FILE) version (* (const version-header)) &return bool) |
|||
(unless (write-introspect-struct-s-expr version-header--metadata |
|||
version out-file |
|||
write-introspect-struct-add-newline) |
|||
(return false)) |
|||
(return true)) |
|||
|
|||
(defun-local check-version (current-version (* (const version-header)) |
|||
read-version-out (* version-header) |
|||
s-expr-in (* (const char)) |
|||
after-version-start-out (* (unsigned int)) |
|||
error-string-out (* char) |
|||
error-string-out-size size_t |
|||
&return versioned-data-result) |
|||
(set (at 0 error-string-out) 0) ;; Clear error if any |
|||
(var result versioned-data-result versioned-data-result-success) |
|||
(unless (read-introspect-struct-s-expr version-header--metadata read-version-out |
|||
s-expr-in malloc after-version-start-out) |
|||
(snprintf error-string-out error-string-out-size |
|||
"error: Failed to read data version header") |
|||
(set result versioned-data-result-version-not-readable) |
|||
(fprintf stderr "%s\n" error-string-out) |
|||
(return result)) |
|||
|
|||
(unless (= (path current-version > version) (path read-version-out > version)) |
|||
(snprintf error-string-out error-string-out-size |
|||
"error: Not going to read data. Version %d does not match expected %d. |
|||
This program's version does not understand data version." |
|||
(path read-version-out > version) (path current-version > version)) |
|||
(set result versioned-data-result-version-mismatch)) |
|||
|
|||
(unless (= result versioned-data-result-success) |
|||
(fprintf stderr "%s\n" error-string-out)) |
|||
|
|||
(return result)) |
|||
|
|||
(defun load-versioned-data (filename (* (const char)) |
|||
current-version (* (const version-header)) |
|||
read-version-out (* version-header) |
|||
data-metadata (* (const metadata-struct)) |
|||
data-out (* void) |
|||
error-buffer (* char) |
|||
error-buffer-size (unsigned int) |
|||
&return versioned-data-result) |
|||
(var result versioned-data-result versioned-data-result-success) |
|||
|
|||
(var in-file (* FILE) (fopen filename "rb")) |
|||
(if in-file |
|||
(scope ;; File valid |
|||
(var file-contents (* (const char)) (read-file-into-memory in-file)) |
|||
(fclose in-file) |
|||
|
|||
(var start-data-offset (unsigned int) 0) |
|||
(var read-version version-header (array 0)) |
|||
(set result (check-version current-version (addr read-version) |
|||
file-contents (addr start-data-offset) |
|||
error-buffer error-buffer-size)) |
|||
(unless (= result versioned-data-result-success) |
|||
(free (type-cast file-contents (* void))) |
|||
(return result)) |
|||
|
|||
;; Read into other data so we don't risk running the app with partially loaded data |
|||
(var possible-data (* void) (malloc (path data-metadata > struct-size))) |
|||
(memset possible-data 0 (path data-metadata > struct-size)) |
|||
(unless (read-introspect-struct-s-expr data-metadata possible-data |
|||
(+ start-data-offset file-contents) malloc null) |
|||
(snprintf error-buffer error-buffer-size |
|||
"error: Failed to read data (parsing error)") |
|||
(fprintf stderr "%s\n" error-buffer) |
|||
(free-introspect-struct-fields data-metadata possible-data free) |
|||
(free possible-data) |
|||
(set result versioned-data-result-cannot-parse-data) |
|||
(return result)) |
|||
(unless (copy-introspect-struct data-metadata data-out possible-data malloc) |
|||
(snprintf error-buffer error-buffer-size |
|||
"error: Failed to copy loaded data into runtime data") |
|||
(fprintf stderr "%s\n" error-buffer) |
|||
(free-introspect-struct-fields data-metadata possible-data free) |
|||
(free possible-data) |
|||
(set result versioned-data-result-cannot-copy-data-into-output) |
|||
(return result)) |
|||
(free-introspect-struct-fields data-metadata possible-data free) |
|||
(free possible-data) |
|||
(free (type-cast file-contents (* void)))) |
|||
(scope ;; File invalid |
|||
(set result versioned-data-result-cannot-open-file))) |
|||
(return result)) |
|||
|
|||
(defun save-versioned-data (filename (* (const char)) |
|||
current-version (* (const version-header)) |
|||
data-metadata (* (const metadata-struct)) |
|||
data-out (* void) |
|||
error-buffer (* char) |
|||
error-buffer-size (unsigned int) |
|||
&return versioned-data-result) |
|||
(var result versioned-data-result versioned-data-result-success) |
|||
(var out-file (* FILE) (fopen filename "wb")) |
|||
(unless out-file |
|||
(snprintf error-buffer error-buffer-size |
|||
"error: failed to save data. File could %s not be opened\n" filename) |
|||
(set result versioned-data-result-cannot-open-file) |
|||
(return result)) |
|||
|
|||
(unless (write-version out-file current-version) |
|||
(fclose out-file) |
|||
(snprintf error-buffer error-buffer-size |
|||
"error: failed to save user data: could not write version header\n") |
|||
(set result versioned-data-result-cannot-write-version) |
|||
(return result)) |
|||
|
|||
(unless (write-introspect-struct-s-expr data-metadata data-out |
|||
out-file write-introspect-struct-add-newline) |
|||
(fclose out-file) |
|||
(snprintf error-buffer error-buffer-size |
|||
"error: failed to save user data\n") |
|||
(return versioned-data-result-cannot-write-data)) |
|||
(fclose out-file) |
|||
(return result)) |
Loading…
Reference in new issue