AmendHub

jcs

/

amend

/

amendments

/

87

repo: Factor out amendment marshalling

I wish I could use bile's marshalling for this, but the current
on-disk format of things is weird in Amend and would require a
database upgrade to convert to bile's way of storing variable-length
fields.
Start on renaming commit to amendment.

jcs made amendment 87 about 1 month ago
--- repo.c Tue Aug 16 14:38:02 2022 +++ repo.c Wed Aug 17 14:23:09 2022 @@ -26,12 +26,12 @@ struct repo * repo_init(struct bile *bile, short is_new); void repo_sort_files(struct repo *repo); -void repo_sort_commits(struct repo *repo); +void repo_sort_amendments(struct repo *repo); short repo_get_file_attrs(struct repo *repo, Str255 filename, struct repo_file_attrs *attrs); short repo_file_update(struct repo *repo, struct repo_file *file); unsigned short repo_diff_header(struct repo *repo, - struct repo_commit *commit, char **ret); + struct repo_amendment *amendment, char **ret); short repo_migrate(struct repo *repo, short is_new); pascal Boolean repo_add_file_filter(struct FileParam *pbp); @@ -103,6 +103,7 @@ repo_init(struct bile *bile, short is_new) Str255 buf; struct bile_object *bob; struct repo *repo; + struct repo_file *file; size_t size; char *data; short error, fh; @@ -111,7 +112,7 @@ repo_init(struct bile *bile, short is_new) repo = xmalloczero(sizeof(struct repo)); repo->bile = bile; repo->next_file_id = 1; - repo->next_commit_id = 1; + repo->next_amendment_id = 1; if (repo_migrate(repo, is_new) != 0) { free(repo); @@ -129,36 +130,37 @@ repo_init(struct bile *bile, short is_new) size = bile_read_alloc(bile, REPO_FILE_RTYPE, bob->id, &data); if (size == 0) panic("failed fetching file %ld", bob->id); + repo->files[i] = xmalloczero(sizeof(struct repo_file)); repo->files[i] = repo_parse_file(bob->id, (unsigned char *)data, size); - if (repo->files[i]->id >= repo->next_file_id) - repo->next_file_id = repo->files[i]->id + 1; + if (repo->files[i]->id >= repo->next_file_id) + repo->next_file_id = repo->files[i]->id + 1; free(data); } } repo_sort_files(repo); - /* fill in commit info */ - repo->ncommits = bile_count_by_type(bile, REPO_COMMIT_RTYPE); - if (repo->ncommits) { - repo->commits = xmalloc(repo->ncommits * sizeof(Ptr)); - for (i = 0; i < repo->ncommits; i++) { - bob = bile_get_nth_of_type(bile, i, REPO_COMMIT_RTYPE); + /* fill in amendment info */ + repo->namendments = bile_count_by_type(bile, REPO_AMENDMENT_RTYPE); + if (repo->namendments) { + repo->amendments = xmalloc(repo->namendments * sizeof(Ptr)); + for (i = 0; i < repo->namendments; i++) { + bob = bile_get_nth_of_type(bile, i, REPO_AMENDMENT_RTYPE); if (bob == NULL) - panic("no %ld commit, but count said it should be there", + panic("no %ld amendment, but count said it should be there", i); - size = bile_read_alloc(bile, REPO_COMMIT_RTYPE, bob->id, + size = bile_read_alloc(bile, REPO_AMENDMENT_RTYPE, bob->id, &data); if (size == 0) - panic("failed fetching commit %ld", bob->id); - repo->commits[i] = repo_parse_commit(bob->id, + panic("failed fetching amendment %ld", bob->id); + repo->amendments[i] = repo_parse_amendment(bob->id, (unsigned char *)data, size); - if (repo->commits[i]->id >= repo->next_commit_id) - repo->next_commit_id = repo->commits[i]->id + 1; + if (repo->amendments[i]->id >= repo->next_amendment_id) + repo->next_amendment_id = repo->amendments[i]->id + 1; free(data); } } - repo_sort_commits(repo); + repo_sort_amendments(repo); return repo; } @@ -167,24 +169,24 @@ void repo_close(struct repo *repo) { struct repo_file *file; - struct repo_commit *commit; + struct repo_amendment *amendment; Handle h; short i; - for (i = 0; i < repo->ncommits; i++) { - commit = repo->commits[i]; - if (commit == NULL) + for (i = 0; i < repo->namendments; i++) { + amendment = repo->amendments[i]; + if (amendment == NULL) continue; - if (commit->log != NULL) - DisposHandle(commit->log); + if (amendment->log != NULL) + DisposHandle(amendment->log); - if (commit->file_ids != NULL) - free(commit->file_ids); + if (amendment->file_ids != NULL) + free(amendment->file_ids); - free(commit); + free(amendment); } - free(repo->commits); + free(repo->amendments); for (i = 0; i < repo->nfiles; i++) { file = repo->files[i]; @@ -248,18 +250,18 @@ repo_parse_file(unsigned long id, unsigned char *data, return file; } -struct repo_commit * -repo_parse_commit(unsigned long id, unsigned char *data, size_t size) +struct repo_amendment * +repo_parse_amendment(unsigned long id, unsigned char *data, size_t size) { - struct repo_commit *commit; + struct repo_amendment *amendment; unsigned short len, i; short datapos; - commit = xmalloczero(sizeof(struct repo_commit)); - commit->id = id; + amendment = xmalloczero(sizeof(struct repo_amendment)); + amendment->id = id; /* date */ - commit->date = ((unsigned long)data[0] << 24) | + amendment->date = ((unsigned long)data[0] << 24) | ((unsigned long)data[1] << 16) | ((unsigned long)data[2] << 8) | ((unsigned long)data[3]); @@ -267,46 +269,46 @@ repo_parse_commit(unsigned long id, unsigned char *dat /* author, pstr */ len = data[0]; - if (len > sizeof(commit->author) - 1) - len = sizeof(commit->author) - 1; - memcpy(commit->author, data + 1, len); - commit->author[len] = '\0'; + if (len > sizeof(amendment->author) - 1) + len = sizeof(amendment->author) - 1; + memcpy(amendment->author, data + 1, len); + amendment->author[len] = '\0'; data += (data[0] + 1); /* files, short */ - commit->nfiles = (data[0] << 8) | data[1]; + amendment->nfiles = (data[0] << 8) | data[1]; data += 2; - if (commit->nfiles) { - commit->file_ids = xmalloc(sizeof(short) * commit->nfiles); - for (i = 0; i < commit->nfiles; i++) { - commit->file_ids[i] = (data[0] << 8) | data[1]; + if (amendment->nfiles) { + amendment->file_ids = xmalloc(sizeof(short) * amendment->nfiles); + for (i = 0; i < amendment->nfiles; i++) { + amendment->file_ids[i] = (data[0] << 8) | data[1]; data += 2; } } /* additions, short */ - commit->adds = (data[0] << 8) | data[1]; + amendment->adds = (data[0] << 8) | data[1]; data += 2; /* subs, short */ - commit->subs = (data[0] << 8) | data[1]; + amendment->subs = (data[0] << 8) | data[1]; data += 2; /* log message, word-length */ len = (data[0] << 8) | data[1]; data += 2; - commit->log = xNewHandle(len + 1); - commit->log_len = len; - HLock(commit->log); - memcpy(*(commit->log), data, len); - (*(commit->log))[len] = '\0'; - HUnlock(commit->log); + amendment->log = xNewHandle(len + 1); + amendment->log_len = len; + HLock(amendment->log); + memcpy(*(amendment->log), data, len); + (*(amendment->log))[len] = '\0'; + HUnlock(amendment->log); data += len; /* TODO: use datapos and check against size like repo_parse_file */ - return commit; + return amendment; } struct repo_file * @@ -322,34 +324,34 @@ repo_file_with_id(struct repo *repo, short id) } unsigned short -repo_diff_header(struct repo *repo, struct repo_commit *commit, +repo_diff_header(struct repo *repo, struct repo_amendment *amendment, char **ret) { struct tm *ttm = NULL; unsigned short header_len; short i; - *ret = xmalloc(128 + commit->log_len); - ttm = localtime(&commit->date); + *ret = xmalloc(128 + amendment->log_len); + ttm = localtime(&amendment->date); header_len = sprintf(*ret, "Author: %s\r" "Date: %04d-%02d-%02d %02d:%02d:%02d\r" "\r ", - commit->author, + amendment->author, ttm->tm_year + 1900, ttm->tm_mon + 1, ttm->tm_mday, ttm->tm_hour, ttm->tm_min, ttm->tm_sec); /* copy log, indenting each line */ - HLock(commit->log); - for (i = 0; i < commit->log_len; i++) { - *(*ret + header_len++) = (*(commit->log))[i]; + HLock(amendment->log); + for (i = 0; i < amendment->log_len; i++) { + *(*ret + header_len++) = (*(amendment->log))[i]; - if ((*(commit->log))[i] == '\r' && i < commit->log_len - 1) { + if ((*(amendment->log))[i] == '\r' && i < amendment->log_len - 1) { *(*ret + header_len++) = ' '; *(*ret + header_len++) = ' '; } } - HUnlock(commit->log); + HUnlock(amendment->log); *(*ret + header_len++) = '\r'; *(*ret + header_len++) = '\r'; @@ -357,7 +359,7 @@ repo_diff_header(struct repo *repo, struct repo_commit } void -repo_show_diff_text(struct repo *repo, struct repo_commit *commit, +repo_show_diff_text(struct repo *repo, struct repo_amendment *amendment, TEHandle te) { char truncbuf[64]; @@ -372,9 +374,9 @@ repo_show_diff_text(struct repo *repo, struct repo_com TESetText("", 0, te); - bob = bile_find(repo->bile, REPO_DIFF_RTYPE, commit->id); + bob = bile_find(repo->bile, REPO_DIFF_RTYPE, amendment->id); if (bob == NULL) { - warn("Failed finding DIFF %d, corrupted repo?", commit->id); + warn("Failed finding DIFF %d, corrupted repo?", amendment->id); return; } @@ -382,7 +384,7 @@ repo_show_diff_text(struct repo *repo, struct repo_com if (diff_len == 0) panic("diff zero bytes"); - header_len = repo_diff_header(repo, commit, &buf); + header_len = repo_diff_header(repo, amendment, &buf); all_len = header_len + diff_len; if (all_len >= MAX_TEXTEDIT_SIZE) { @@ -397,7 +399,7 @@ repo_show_diff_text(struct repo *repo, struct repo_com size = bile_read_object(repo->bile, bob, dtext + header_len, all_len - header_len); if (size == 0) - panic("failed reading diff %lu: %d", commit->id, + panic("failed reading diff %lu: %d", amendment->id, bile_error(repo->bile)); if (trunc) { @@ -698,18 +700,18 @@ repo_sort_files(struct repo *repo) } void -repo_sort_commits(struct repo *repo) +repo_sort_amendments(struct repo *repo) { - struct repo_commit *commit; + struct repo_amendment *amendment; short i, j; - /* reverse order, newest commit first */ - for (i = 0; i < repo->ncommits; i++) { - for (j = 0; j < repo->ncommits - i - 1; j++) { - if (repo->commits[j]->id < repo->commits[j + 1]->id) { - commit = repo->commits[j]; - repo->commits[j] = repo->commits[j + 1]; - repo->commits[j + 1] = commit; + /* reverse order, newest amendment first */ + for (i = 0; i < repo->namendments; i++) { + for (j = 0; j < repo->namendments - i - 1; j++) { + if (repo->amendments[j]->id < repo->amendments[j + 1]->id) { + amendment = repo->amendments[j]; + repo->amendments[j] = repo->amendments[j + 1]; + repo->amendments[j + 1] = amendment; } } } @@ -872,7 +874,7 @@ repo_file_changed(struct repo *repo, struct repo_file } void -repo_export_patch(struct repo *repo, struct repo_commit *commit, +repo_export_patch(struct repo *repo, struct repo_amendment *amendment, short vrefnum, Str255 filename) { struct bile_object *bob; @@ -880,9 +882,9 @@ repo_export_patch(struct repo *repo, struct repo_commi char *buf = NULL; short error, frefnum, header_len; - bob = bile_find(repo->bile, REPO_DIFF_RTYPE, commit->id); + bob = bile_find(repo->bile, REPO_DIFF_RTYPE, amendment->id); if (bob == NULL) - panic("failed finding DIFF %d", commit->id); + panic("failed finding DIFF %d", amendment->id); /* * Don't use our creator here because we don't want finder opening us @@ -904,7 +906,7 @@ repo_export_patch(struct repo *repo, struct repo_commi panic("Failed to truncate file %s: %d", PtoCstr(filename), error); - size = repo_diff_header(repo, commit, &buf); + size = repo_diff_header(repo, amendment, &buf); error = FSWrite(frefnum, &size, buf); if (error) panic("Failed to write diff header to %s: %d", PtoCstr(filename), @@ -923,105 +925,70 @@ repo_export_patch(struct repo *repo, struct repo_commi } void -repo_commit(struct repo *repo, struct diffed_file *diffed_files, +repo_amend(struct repo *repo, struct diffed_file *diffed_files, short nfiles, short adds, short subs, char *author, Handle log, short loglen, Handle diff, unsigned long difflen) { Str255 tfilename; - unsigned char *commit, *tdata, *fdata; + struct repo_amendment *amendment; + unsigned long datalen, fsize; + unsigned char *tdata, *fdata; + char *amendment_data; FInfo finfo; size_t size; - short commit_len, pos = 0, len; - short i, commit_id, id, error, frefnum, actual_nfiles; - time_t date; - long fsize; + short i, id, error, frefnum; - /* date (long) */ - commit_len = sizeof(long); - - /* author (pstr) */ - commit_len += 1 + strlen(author); + amendment = xmalloczero(sizeof(struct repo_amendment)); + amendment->id = repo->next_amendment_id; + amendment->date = Time; /* find files with actual data changes */ - actual_nfiles = 0; + amendment->nfiles = 0; + amendment->file_ids = xcalloc(sizeof(short), nfiles); for (i = 0; i < nfiles; i++) { - if (diffed_files[i].flags & DIFFED_FILE_TEXT) - actual_nfiles++; + if (diffed_files[i].flags & DIFFED_FILE_TEXT) { + amendment->file_ids[amendment->nfiles] = + diffed_files[i].file->id; + amendment->nfiles++; + } } - if (actual_nfiles == 0) - panic("repo_commit passed nfiles %d but actual files is 0", nfiles); + if (amendment->nfiles == 0) + panic("repo_amendment passed nfiles %d but actual files is 0", + nfiles); - /* nfiles (short) */ - commit_len += sizeof(short) + (actual_nfiles * sizeof(short)); + strlcpy(amendment->author, author, sizeof(amendment->author)); + amendment->adds = adds; + amendment->subs = subs; - /* adds (short) */ - commit_len += sizeof(short); - - /* deletions (short) */ - commit_len += sizeof(short); - - /* log (wstr) */ - commit_len += sizeof(short) + loglen; - - commit = xmalloc(commit_len); - - date = Time; - commit[pos++] = (date >> 24) & 0xff; - commit[pos++] = (date >> 16) & 0xff; - commit[pos++] = (date >> 8) & 0xff; - commit[pos++] = date & 0xff; - - commit[pos++] = strlen(author); - for (i = 0; i < strlen(author); i++) - commit[pos++] = author[i]; - - commit[pos++] = (actual_nfiles >> 8) & 0xff; - commit[pos++] = actual_nfiles & 0xff; - for (i = 0; i < nfiles; i++) { - if (!(diffed_files[i].flags & DIFFED_FILE_TEXT)) - continue; - - commit[pos++] = (diffed_files[i].file->id >> 8) & 0xff; - commit[pos++] = diffed_files[i].file->id & 0xff; - } - - commit[pos++] = (adds >> 8) & 0xff; - commit[pos++] = adds & 0xff; - - commit[pos++] = (subs >> 8) & 0xff; - commit[pos++] = subs & 0xff; - + /* caller expects to be able to free their version, so make our own */ + amendment->log_len = loglen; + amendment->log = xNewHandle(loglen); HLock(log); - commit[pos++] = (loglen >> 8) & 0xff; - commit[pos++] = loglen & 0xff; - memcpy(commit + pos, *log, loglen); - pos += loglen; + HLock(amendment->log); + memcpy(*(amendment->log), *log, loglen); + HUnlock(amendment->log); HUnlock(log); - if (pos != commit_len) - panic("repo_commit: accumulated len %d != expected %d", pos, - commit_len); + repo_marshall_amendment(amendment, &amendment_data, &datalen); - commit_id = repo->next_commit_id; - /* store diff */ HLock(diff); progress("Storing diff..."); - size = bile_write(repo->bile, REPO_DIFF_RTYPE, commit_id, *diff, + size = bile_write(repo->bile, REPO_DIFF_RTYPE, amendment->id, *diff, difflen); if (size != difflen) panic("Failed storing diff in repo file: %d", bile_error(repo->bile)); HUnlock(diff); - /* store commit */ - progress("Storing commit metadata..."); - size = bile_write(repo->bile, REPO_COMMIT_RTYPE, commit_id, commit, - commit_len); - if (size != commit_len) - panic("Failed storing commit in repo file: %d", + /* store amendment */ + progress("Storing amendment metadata..."); + size = bile_write(repo->bile, REPO_AMENDMENT_RTYPE, amendment->id, + amendment_data, datalen); + if (size != datalen) + panic("Failed storing amendment in repo file: %d", bile_error(repo->bile)); - /* used later, don't free yet */ + free(amendment_data); /* store new versions of each file */ for (i = 0; i < nfiles; i++) { @@ -1072,18 +1039,82 @@ repo_commit(struct repo *repo, struct diffed_file *dif /* flush volume */ bile_flush(repo->bile, 1); - repo->next_commit_id = commit_id + 1; + repo->next_amendment_id = amendment->id + 1; - /* update commit list */ - repo->ncommits++; - repo->commits = xreallocarray(repo->commits, repo->ncommits, + /* update amendment list */ + repo->namendments++; + repo->amendments = xreallocarray(repo->amendments, repo->namendments, sizeof(Ptr)); - repo->commits[repo->ncommits - 1] = repo_parse_commit(commit_id, - commit, commit_len); + repo->amendments[repo->namendments - 1] = amendment; - free(commit); + repo_sort_amendments(repo); +} - repo_sort_commits(repo); +void +repo_marshall_amendment(struct repo_amendment *amendment, char **retdata, + unsigned long *retlen) +{ + unsigned short len, pos = 0; + char *data; + short i; + char clen; + + /* date (long) */ + len = sizeof(long); + + /* author (pstr) */ + len += 1 + strlen(amendment->author); + + /* nfiles (short) */ + len += sizeof(short) + (amendment->nfiles * sizeof(short)); + + /* adds (short) */ + len += sizeof(short); + + /* deletions (short) */ + len += sizeof(short); + + /* log (wstr) */ + len += sizeof(short) + amendment->log_len; + + *retdata = xmalloc(len); + data = *retdata; + + data[pos++] = (amendment->date >> 24) & 0xff; + data[pos++] = (amendment->date >> 16) & 0xff; + data[pos++] = (amendment->date >> 8) & 0xff; + data[pos++] = amendment->date & 0xff; + + clen = strlen(amendment->author); + data[pos++] = clen; + for (i = 0; i < clen; i++) + data[pos++] = amendment->author[i]; + + data[pos++] = (amendment->nfiles >> 8) & 0xff; + data[pos++] = amendment->nfiles & 0xff; + for (i = 0; i < amendment->nfiles; i++) { + data[pos++] = (amendment->file_ids[i] >> 8) & 0xff; + data[pos++] = amendment->file_ids[i] & 0xff; + } + + data[pos++] = (amendment->adds >> 8) & 0xff; + data[pos++] = amendment->adds & 0xff; + + data[pos++] = (amendment->subs >> 8) & 0xff; + data[pos++] = amendment->subs & 0xff; + + HLock(amendment->log); + data[pos++] = (amendment->log_len >> 8) & 0xff; + data[pos++] = amendment->log_len & 0xff; + memcpy(data + pos, *(amendment->log), amendment->log_len); + pos += amendment->log_len; + HUnlock(amendment->log); + + if (pos != len) + panic("repo_marshall_amendment: accumulated len %d != expected %d", + pos, len); + + *retlen = len; } short --- repo.h Wed Jun 15 10:39:56 2022 +++ repo.h Wed Aug 17 13:42:43 2022 @@ -25,7 +25,7 @@ #define REPO_TYPE 'AMRP' #define REPO_FILE_RTYPE 'AFIL' -#define REPO_COMMIT_RTYPE 'CMMT' +#define REPO_AMENDMENT_RTYPE 'CMMT' #define REPO_DIFF_RTYPE 'DIFF' #define REPO_TEXT_RTYPE 'TEXT' #define REPO_VERS_RTYPE 'RVER' @@ -61,7 +61,7 @@ struct diffed_file { #define DIFFED_FILE_METADATA (1 << 1) }; -struct repo_commit { +struct repo_amendment { short id; time_t date; char author[32]; @@ -78,20 +78,20 @@ struct repo { short nfiles; struct repo_file **files; short next_file_id; - short ncommits; - struct repo_commit **commits; - short next_commit_id; + short namendments; + struct repo_amendment **amendments; + short next_amendment_id; }; struct repo *repo_open(AppFile *file); struct repo *repo_create(void); void repo_close(struct repo *repo); -struct repo_commit *repo_parse_commit(unsigned long id, unsigned char *data, +struct repo_amendment *repo_parse_amendment(unsigned long id, unsigned char *data, size_t size); struct repo_file * repo_parse_file(unsigned long id, unsigned char *data, size_t size); struct repo_file *repo_file_with_id(struct repo *repo, short id); -void repo_show_diff_text(struct repo *repo, struct repo_commit *commit, +void repo_show_diff_text(struct repo *repo, struct repo_amendment *amendment, TEHandle te); struct repo_file *repo_add_file(struct repo *repo); void repo_file_mark_for_deletion(struct repo *repo, struct repo_file *file); @@ -99,11 +99,13 @@ short repo_diff_file(struct repo *repo, struct repo_fi short repo_file_changed(struct repo *repo, struct repo_file *file); short repo_checkout_file(struct repo *repo, struct repo_file *file, short vrefnum, Str255 filename); -void repo_export_patch(struct repo *repo, struct repo_commit *commit, +void repo_export_patch(struct repo *repo, struct repo_amendment *amendment, short vrefnum, Str255 filename); -void repo_commit(struct repo *repo, struct diffed_file *diffed_files, +void repo_amend(struct repo *repo, struct diffed_file *diffed_files, short nfiles, short adds, short subs, char *author, Handle log, short loglen, Handle diff, unsigned long difflen); +void repo_marshall_amendment(struct repo_amendment *amendment, + char **retdata, unsigned long *retlen); void repo_backup(struct repo *repo); #endif