summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libstore/rdwr.c161
1 files changed, 91 insertions, 70 deletions
diff --git a/libstore/rdwr.c b/libstore/rdwr.c
index 9d791d28..b9e51fd2 100644
--- a/libstore/rdwr.c
+++ b/libstore/rdwr.c
@@ -26,12 +26,24 @@
/* Returns in RUNS the tail of STORE's run list, who's first run contains
ADDR, and is not a whole, and in RUNS_END a pointer pointing at the end of
- the run list. Returns the offset within it at which ADDR occurs. */
+ the run list. Returns the offset within it at which ADDR occurs. Also
+ returns BASE, which should be added to offsets from RUNS. */
static inline off_t
store_find_first_run (struct store *store, off_t addr,
- off_t **runs, off_t **runs_end)
+ off_t **runs, off_t **runs_end,
+ off_t *base, size_t *index)
{
off_t *tail = store->runs, *tail_end = tail + store->runs_len;
+ off_t wrap = store->wrap;
+
+ if (addr >= wrap && addr < store->end)
+ /* Locate the correct position within a repeating pattern of runs. */
+ {
+ *base = addr / wrap;
+ addr %= wrap;
+ }
+ else
+ *base = 0;
/* XXX: this isn't going to be very efficient if RUNS is very complex...
But it should do dandy if it's short. For long run lists, we could do a
@@ -44,6 +56,7 @@ store_find_first_run (struct store *store, off_t addr,
{
*runs = tail;
*runs_end = tail_end;
+ *index = ((tail - store->runs) >> 1);
return addr;
}
@@ -54,6 +67,28 @@ store_find_first_run (struct store *store, off_t addr,
return -1;
}
+
+/* Update RUNS, BASE, & INDEX to point to the next elemement in the runs
+ array. RUNS_END is the point where RUNS will wrap. Returns true if
+ things are still kosher. */
+static inline int
+store_next_run (struct store *store, off_t *runs_end,
+ off_t **runs, off_t *base, size_t *index)
+{
+ *runs += 2;
+ *index += 1;
+
+ if (*runs == runs_end)
+ /* Wrap around in a repeating RUNS. */
+ {
+ *runs = store->runs;
+ *base += store->wrap;
+ *index = 0;
+ return (*base < store->end);
+ }
+ else
+ return 1;
+}
/* Write LEN bytes from BUF to STORE at ADDR. Returns the amount written
in AMOUNT. ADDR is in BLOCKS (as defined by STORE->block_size). */
@@ -62,64 +97,54 @@ store_write (struct store *store,
off_t addr, char *buf, size_t len, size_t *amount)
{
error_t err;
- off_t *runs, *runs_end;
+ size_t index;
+ off_t *runs, *runs_end, base;
+ int block_shift = store->log2_block_size;
store_write_meth_t write = store->meths->write;
- addr = store_find_first_run (store, addr, &runs, &runs_end);
+ addr = store_find_first_run (store, addr, &runs, &runs_end, &base, &index);
if (addr < 0)
err = EIO;
- else if (runs[1] >= len)
+ else if ((runs[1] << block_shift) >= len)
/* The first run has it all... */
- err = (*write)(store, runs[0] + addr, buf, len, amount);
+ err = (*write)(store, base + runs[0] + addr, index, buf, len, amount);
else
/* ARGH, we've got to split up the write ... */
{
- mach_msg_type_number_t written;
+ mach_msg_type_number_t try = runs[1] << block_shift, written;
/* Write the initial bit in the first run. Errors here are returned. */
- err = (*write)(store, runs[0] + addr, buf, runs[1], &written);
+ err = (*write)(store, base + runs[0] + addr, index, buf, try, &written);
- if (!err && written == runs[1])
+ if (!err && written == try)
/* Wrote the first bit successfully, now do the rest. Any errors
will just result in a short write. */
{
- int block_shift = store->log2_block_size;
-
buf += written;
len -= written;
- runs += 2;
- while (runs != runs_end)
+ while (store_next_run (store, runs_end, &runs, &base, &index)
+ && runs[0] >= 0) /* Check for holes. */
+ /* Ok, we can write in this run, at least a bit. */
{
- off_t run_addr = runs[0];
- off_t run_blocks = runs[1];
-
- if (run_addr < 0)
- /* A hole! Can't write here. Must stop. */
- break;
- else
- /* Ok, we can write in this run, at least a bit. */
- {
- mach_msg_type_number_t seg_written;
- off_t run_len = (run_blocks << block_shift);
- size_t seg_len = run_len > len ? len : run_len;
-
- err = (*write)(store, run_addr, buf, seg_len, &seg_written);
- if (err)
- break; /* Ack */
-
- written += seg_written;
- if (seg_written < run_len)
- break; /* Didn't use up the run, we're done. */
-
- len -= seg_written;
- if (len == 0)
- break; /* Nothing left to write! */
-
- buf += written;
- }
-
- runs += 2;
+ mach_msg_type_number_t seg_written;
+ off_t run_len = (runs[1] << block_shift);
+
+ try = run_len > len ? len : run_len;
+ err = (*write)(store, base + runs[0], index, buf, try,
+ &seg_written);
+ if (err)
+ break; /* Ack */
+
+ written += seg_written;
+ len -= seg_written;
+ if (len == 0)
+ break; /* Nothing left to write! */
+
+ if (seg_written < run_len)
+ break; /* Didn't use up the run, we're done. */
+
+ buf += seg_written;
}
}
@@ -137,15 +162,17 @@ store_read (struct store *store,
off_t addr, size_t amount, char **buf, size_t *len)
{
error_t err;
- off_t *runs, *runs_end;
+ size_t index;
+ off_t *runs, *runs_end, base;
+ int block_shift = store->log2_block_size;
store_read_meth_t read = store->meths->read;
- addr = store_find_first_run (store, addr, &runs, &runs_end);
+ addr = store_find_first_run (store, addr, &runs, &runs_end, &base, &index);
if (addr < 0)
err = EIO;
- else if (runs[1] >= amount)
+ else if ((runs[1] << block_shift) >= amount)
/* The first run has it all... */
- err = (*read)(store, runs[0] + addr, amount, buf, len);
+ err = (*read)(store, base + runs[0] + addr, index, amount, buf, len);
else
/* ARGH, we've got to split up the read ... This isn't fun. */
{
@@ -153,9 +180,8 @@ store_read (struct store *store,
/* WHOLE_BUF and WHOLE_BUF_LEN will point to a buff that's large enough
to hold the entire request. This is initially whatever the user
passed in, but we'll change it as necessary. */
- char *whole_buf = *buf, *buf_end = whole_buf;
+ char *whole_buf = *buf, *buf_end;
size_t whole_buf_len = *len;
- int block_shift = store->log2_block_size;
/* Read LEN bytes from the store address ADDR into BUF_END. BUF_END
and AMOUNT are adjusted by the amount actually read. Whether or not
@@ -166,7 +192,8 @@ store_read (struct store *store,
whole (within one run). */
char *seg_buf = buf_end;
size_t seg_buf_len = len;
- error_t err = (*read)(store, addr, len, &seg_buf, &seg_buf_len);
+ error_t err =
+ (*read)(store, addr, index, len, &seg_buf, &seg_buf_len);
if (!err)
{
/* If for some bizarre reason, the underlying storage chose not
@@ -192,34 +219,28 @@ store_read (struct store *store,
return err; /* Punt early, there's nothing to clean up. */
}
- err = seg_read (runs[0] + addr, runs[1] << block_shift, &all);
+ buf_end = whole_buf;
- if (!err && all)
+ err = seg_read (base + runs[0] + addr, runs[1] << block_shift, &all);
+ while (!err && all && amount > 0
+ && store_next_run (store, runs_end, &runs, &base, &index))
{
- runs += 2;
- while (!err && runs != runs_end && all)
+ off_t run_addr = runs[0];
+ off_t run_blocks = runs[1];
+
+ if (run_addr < 0)
+ /* A hole! Can't read here. Must stop. */
+ break;
+ else
{
- off_t run_addr = runs[0];
- off_t run_blocks = runs[1];
-
- if (run_addr < 0)
- /* A hole! Can't read here. Must stop. */
- break;
- else if (amount == 0)
- break;
- else
- {
- off_t run_len = (run_blocks << block_shift);
- off_t seg_len = run_len > amount ? amount : run_len;
- err = seg_read (run_addr, seg_len, &all);
- }
-
- runs +=2;
+ off_t run_len = (run_blocks << block_shift);
+ off_t seg_len = run_len > amount ? amount : run_len;
+ err = seg_read (base + run_addr, seg_len, &all);
}
}
/* The actual amount read. */
- *len = whole_buf + whole_buf_len - buf_end;
+ *len = buf_end - whole_buf;
if (*len > 0)
err = 0; /* Return a short read instead of an error. */