// Free memory associated with a 'stepcompress' object public void Dispose() { Stepcompress.free(queue); }
// Find a 'step_move' that covers a series of step times step_move compress_bisect_add() { uint *qlast = queue_next; if (qlast > queue_pos + 65535) { qlast = queue_pos + 65535; } points point = minmax_point(queue_pos); int outer_mininterval = point.minp, outer_maxinterval = point.maxp; int add = 0, minadd = -0x8000, maxadd = 0x7fff; int bestinterval = 0, bestcount = 1, bestadd = 1, bestreach = int.MinValue; int zerointerval = 0, zerocount = 0; int count, addfactor, nextaddfactor, c; while (true) { // Find longest valid sequence with the given 'add' points nextpoint; int nextmininterval = outer_mininterval; int nextmaxinterval = outer_maxinterval, interval = nextmaxinterval; int nextcount = 1; for (; ;) { nextcount++; if (&queue_pos[nextcount - 1] >= qlast) { count = nextcount - 1; return(new step_move((uint)interval, (ushort)count, (short)add)); } nextpoint = minmax_point(queue_pos + nextcount - 1); nextaddfactor = nextcount * (nextcount - 1) / 2; c = add * nextaddfactor; if (nextmininterval * nextcount < nextpoint.minp - c) { nextmininterval = Stepcompress.DIV_ROUND_UP(nextpoint.minp - c, nextcount); } if (nextmaxinterval * nextcount > nextpoint.maxp - c) { nextmaxinterval = (nextpoint.maxp - c) / nextcount; } if (nextmininterval > nextmaxinterval) { break; } interval = nextmaxinterval; } // Check if this is the best sequence found so far count = nextcount - 1; addfactor = count * (count - 1) / 2; int reach = add * addfactor + interval * count; if (reach > bestreach || (reach == bestreach && interval > bestinterval)) { bestinterval = interval; bestcount = count; bestadd = add; bestreach = reach; if (add == 0) { zerointerval = interval; zerocount = count; } if (count > 0x200) { // No 'add' will improve sequence; avoid integer overflow break; } } // Check if a greater or lesser add could extend the sequence nextaddfactor = nextcount * (nextcount - 1) / 2; int nextreach = add * nextaddfactor + interval * nextcount; if (nextreach < nextpoint.minp) { minadd = add + 1; outer_maxinterval = nextmaxinterval; } else { maxadd = add - 1; outer_mininterval = nextmininterval; } // The maximum valid deviation between two quadratic sequences // can be calculated and used to further limit the add range. if (count > 1) { int errdelta = (int)max_error * Stepcompress.QUADRATIC_DEV / (count * count); if (minadd < add - errdelta) { minadd = add - errdelta; } if (maxadd > add + errdelta) { maxadd = add + errdelta; } } // See if next point would further limit the add range c = outer_maxinterval * nextcount; if (minadd * nextaddfactor < nextpoint.minp - c) { minadd = Stepcompress.idiv_up(nextpoint.minp - c, nextaddfactor); } c = outer_mininterval * nextcount; if (maxadd * nextaddfactor > nextpoint.maxp - c) { maxadd = Stepcompress.idiv_down(nextpoint.maxp - c, nextaddfactor); } // Bisect valid add range and try again with new 'add' if (minadd > maxadd) { break; } add = maxadd - (maxadd - minadd) / 4; } if (zerocount + zerocount / 16 >= bestcount) { // Prefer add=0 if it's similar to the best found sequence return(new step_move((uint)zerointerval, (ushort)zerocount, 0)); } return(new step_move((uint)bestinterval, (ushort)bestcount, (short)bestadd)); }
// Slow path for queue_append() public int queue_append_slow(double rel_sc) { ulong abs_step_clock = (ulong)(rel_sc + last_step_clock); if (abs_step_clock >= last_step_clock + Stepcompress.CLOCK_DIFF_MAX) { // Avoid integer overflow on steps far in the future int ret = flush(abs_step_clock - Stepcompress.CLOCK_DIFF_MAX + 1); if (ret != 0) { return(ret); } if (abs_step_clock >= last_step_clock + Stepcompress.CLOCK_DIFF_MAX) { return(flush_far(abs_step_clock)); } } if (queue_next - queue_pos > 65535 + 2000) { // No point in keeping more than 64K steps in memory uint flush = *(queue_next - 65535) - (uint)last_step_clock; int ret = this.flush(last_step_clock + flush); if (ret != 0) { return(ret); } } if (queue_next >= queue_end) { // Make room in the queue long in_use = queue_next - queue_pos; if (queue_pos > queue) { // Shuffle the internal queue to avoid having to allocate more ram Stepcompress.memmove(queue, queue_pos, in_use * sizeof(uint)); } else { // Expand the internal queue of step times long alloc = queue_end - queue; if (alloc == 0) { alloc = Stepcompress.QUEUE_START_SIZE; } while (in_use >= alloc) { alloc *= 2; } queue = (uint *)Stepcompress.realloc(queue, alloc * sizeof(uint)); queue_end = queue + alloc; } queue_pos = queue; queue_next = queue + in_use; } *queue_next++ = (uint)abs_step_clock; return(0); }