/*
* Empire - A multi-player, client/server Internet based war game.
- * Copyright (C) 1986-2013, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ * Copyright (C) 1986-2020, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure, Markus Armbruster
*
* Empire is free software: you can redistribute it and/or modify
* Known contributors to this file:
* Dave Pare, 1989
* Steve McClure, 2000
- * Markus Armbruster, 2005-2012
+ * Markus Armbruster, 2005-2014
*/
#include <config.h>
static unsigned ef_generation;
/*
- * Open the file-backed table TYPE (EF_SECTOR, ...).
- * HOW are flags to control operation. Naturally, immutable flags are
+ * Open the file-backed table @type (EF_SECTOR, ...).
+ * @how are flags to control operation. Naturally, immutable flags are
* not permitted.
* The table must not be already open.
* Return non-zero on success, zero on failure.
ep = &empfile[type];
if (CANT_HAPPEN(!ep->file || ep->base != EF_BAD || ep->fd >= 0))
return 0;
+ if (CANT_HAPPEN(ep->prewrite && !(how & EFF_MEM)))
+ return 0; /* not implemented */
oflags = O_RDWR;
if (how & EFF_PRIVATE)
oflags = O_RDONLY;
}
/*
- * Reallocate cache for table EP to hold COUNT slots.
+ * Reallocate cache for table @ep to hold @count slots.
* The table must not be allocated statically.
* The cache may still be unmapped.
* If reallocation succeeds, any pointers obtained from ef_ptr()
}
/*
- * Open the table TYPE, which is a view of a base table
+ * Open the table @type, which is a view of a base table
* The table must not be already open.
* Return non-zero on success, zero on failure.
* Beware: views work only as long as the base table doesn't change size!
}
/*
- * Close the open table TYPE (EF_SECTOR, ...).
+ * Close the open table @type (EF_SECTOR, ...).
* Return non-zero on success, zero on failure.
*/
int
}
/*
- * Flush file-backed table TYPE (EF_SECTOR, ...) to its backing file.
+ * Flush file-backed table @type (EF_SECTOR, ...) to its backing file.
* Do nothing if the table is privately mapped.
* Update timestamps of written elements if table is EFF_TYPED.
* Return non-zero on success, zero on failure.
}
/*
- * Return pointer to element ID in table TYPE if it exists, else NULL.
+ * Return pointer to element @id in table @type if it exists, else NULL.
* The table must be fully cached, i.e. flags & EFF_MEM.
* The caller is responsible for flushing changes he makes.
*/
}
/*
- * Read element ID from table TYPE into buffer INTO.
+ * Read element @id from table @type into buffer @into.
* FIXME pass buffer size!
- * INTO is marked fresh with ef_mark_fresh().
+ * @into is marked fresh with ef_mark_fresh().
* Return non-zero on success, zero on failure.
*/
int
}
/*
- * Fill cache of file-backed EP with elements starting at ID.
+ * Fill cache of file-backed @ep with elements starting at @id.
* If any were read, return their number.
* Else return -1 and leave the cache unchanged.
*/
}
/*
- * Write COUNT elements starting at ID from BUF to file-backed EP.
+ * Write @count elements starting at @id from @buf to file-backed @ep.
* Update the timestamp if the table is EFF_TYPED.
* Don't actually write if table is privately mapped.
* Return 0 on success, -1 on error (file may be corrupt then).
{
int i, n, ret;
char *p;
- struct emptypedstr *elt;
+ struct ef_typedstr *elt;
time_t now;
if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
* TODO Oopses here could be due to bad data corruption.
* Fail instead of attempting to recover?
*/
- elt = (struct emptypedstr *)((char *)buf + i * ep->size);
+ elt = (struct ef_typedstr *)((char *)buf + i * ep->size);
if (CANT_HAPPEN(elt->ef_type != ep->uid))
elt->ef_type = ep->uid;
if (CANT_HAPPEN(elt->uid != id + i))
}
/*
- * Write element ID into table TYPE from buffer FROM.
+ * Write element @id into table @type from buffer @from.
* FIXME pass buffer size!
- * Update timestamp in FROM if table is EFF_TYPED.
+ * Update timestamp in @from if table is EFF_TYPED.
* If table is file-backed and not privately mapped, write through
* cache straight to disk.
* Cannot write beyond the end of fully cached table (flags & EFF_MEM).
* Can write at the end of partially cached table.
- * FROM must be fresh; see ef_make_stale().
+ * @from must be fresh; see ef_make_stale().
* Return non-zero on success, zero on failure.
*/
int
ep = &empfile[type];
if (CANT_HAPPEN((ep->flags & (EFF_MEM | EFF_PRIVATE)) == EFF_PRIVATE))
return 0;
+ if (CANT_HAPPEN(id < 0))
+ return 0;
if (CANT_HAPPEN(ep->nent >= 0 && id >= ep->nent))
return 0; /* beyond fixed size */
new_seqno(ep, from);
}
/*
- * Change element id.
- * BUF is an element of table TYPE.
- * ID is its new element ID.
- * If table is EFF_TYPED, change id and sequence number stored in BUF.
+ * Change element ID.
+ * @buf is an element of table @type.
+ * @id is its new element ID.
+ * If table is EFF_TYPED, change ID and sequence number stored in @buf.
* Else do nothing.
*/
void
ef_set_uid(int type, void *buf, int uid)
{
- struct emptypedstr *elt;
+ struct ef_typedstr *elt;
struct empfile *ep;
if (ef_check(type) < 0)
}
/*
- * Return sequence number of element ID in table EP.
+ * Are *@a and *@b equal, except for timestamps and such?
+ */
+int
+ef_typedstr_eq(struct ef_typedstr *a, struct ef_typedstr *b)
+{
+ return a->ef_type == b->ef_type
+ && a->seqno == b->seqno
+ && a->uid == b->uid
+ && !memcmp((char *)a + sizeof(*a), (char *)b + sizeof(*a),
+ empfile[a->ef_type].size - sizeof(*a));
+}
+
+/*
+ * Return sequence number of element @id in table @ep.
* Return zero if table is not EFF_TYPED (it has no sequence number
* then).
*/
static unsigned
get_seqno(struct empfile *ep, int id)
{
- struct emptypedstr *elt;
+ struct ef_typedstr *elt;
if (!(ep->flags & EFF_TYPED))
return 0;
}
/*
- * Increment sequence number in BUF, which is about to be written to EP.
+ * Increment sequence number in @buf, which is about to be written to @ep.
* Do nothing if table is not EFF_TYPED (it has no sequence number
* then).
- * Else, BUF's sequence number must match the one in EP's cache. If
+ * Else, @buf's sequence number must match the one in @ep's cache. If
* it doesn't, we're about to clobber a previous write.
*/
static void
new_seqno(struct empfile *ep, void *buf)
{
- struct emptypedstr *elt = buf;
+ struct ef_typedstr *elt = buf;
unsigned old_seqno;
if (!(ep->flags & EFF_TYPED))
ef_generation++;
}
-/* Mark copy of an element of table TYPE in BUF fresh. */
+/* Mark copy of an element of table @type in @buf fresh. */
void
ef_mark_fresh(int type, void *buf)
{
ep = &empfile[type];
if (!(ep->flags & EFF_TYPED))
return;
- ((struct emptypedstr *)buf)->generation = ef_generation;
+ ((struct ef_typedstr *)buf)->generation = ef_generation;
}
static void
must_be_fresh(struct empfile *ep, void *buf)
{
- struct emptypedstr *elt = buf;
+ struct ef_typedstr *elt = buf;
if (!(ep->flags & EFF_TYPED))
return;
}
/*
- * Extend table TYPE by COUNT elements.
+ * Extend table @type by @count elements.
* Any pointers obtained from ef_ptr() become invalid.
* Return non-zero on success, zero on failure.
*/
}
/*
- * Initialize element ID for table TYPE in BUF.
+ * Initialize element @id for table @type in @buf.
* FIXME pass buffer size!
- * BUF is marked fresh with ef_mark_fresh().
+ * @buf is marked fresh with ef_mark_fresh().
*/
void
ef_blank(int type, int id, void *buf)
{
struct empfile *ep;
- struct emptypedstr *elt;
+ struct ef_typedstr *elt;
if (ef_check(type) < 0)
return;
}
/*
- * Initialize COUNT elements of EP in BUF, starting with element ID.
+ * Initialize @count elements of @ep in @buf, starting with element @id.
*/
static void
do_blank(struct empfile *ep, void *buf, int id, int count)
{
int i;
- struct emptypedstr *elt;
+ struct ef_typedstr *elt;
memset(buf, 0, count * ep->size);
for (i = 0; i < count; i++) {
- elt = (struct emptypedstr *)((char *)buf + i * ep->size);
+ elt = (struct ef_typedstr *)((char *)buf + i * ep->size);
if (ep->flags & EFF_TYPED) {
elt->ef_type = ep->uid;
elt->uid = id + i;
}
/*
- * Truncate table TYPE to COUNT elements.
+ * Truncate table @type to @count elements.
* Any pointers obtained from ef_ptr() become invalid.
* Return non-zero on success, zero on failure.
*/
}
/*
- * Search for a table matching NAME, return its table type.
+ * Search for a table matching @name, return its table type.
* Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
* several.
*/
}
/*
- * Search CHOICES[] for a table type matching NAME, return it.
+ * Search @choices[] for a table type matching @name, return it.
* Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
* several.
- * CHOICES[] must be terminated with a negative value.
+ * @choices[] must be terminated with a negative value.
*/
int
ef_byname_from(char *name, int choices[])
}
/*
- * Return name of table TYPE. Always a single, short word.
+ * Return name of table @type. Always a single, short word.
*/
char *
ef_nameof(int type)
}
/*
- * Return "pretty" name of table TYPE.
+ * Return "pretty" name of table @type.
*/
char *
ef_nameof_pretty(int type)
}
/*
- * Ensure table contains element ID.
- * If necessary, extend it in steps of COUNT elements.
+ * Ensure table @type contains element @id.
+ * If necessary, extend it in steps of @count elements.
* Return non-zero on success, zero on failure.
*/
int
ef_ensure_space(int type, int id, int count)
{
- if (ef_check(type) < 0)
+ if (ef_check(type) < 0 || CANT_HAPPEN(id < 0))
return 0;
- CANT_HAPPEN(id < 0);
while (id >= empfile[type].fids) {
if (!ef_extend(type, count))
}
/*
- * Return maximum ID acceptable for table TYPE.
+ * Return maximum ID acceptable for table @type.
* Assuming infinite memory and disk space.
*/
int