+ if (!first_change_only) {
+ const bool end_sample = get_unpacked_sample(end) & sig_mask;
+ if (last_sample != end_sample)
+ edges.emplace_back(end, end_sample);
+ edges.emplace_back(end + 1, end_sample);
+ }
+}
+
+void LogicSegment::get_surrounding_edges(vector<EdgePair> &dest,
+ uint64_t origin_sample, float min_length, int sig_index)
+{
+ if (origin_sample >= sample_count_)
+ return;
+
+ // Put the edges vector on the heap, it can become quite big until we can
+ // use a get_subsampled_edges() implementation that searches backwards
+ vector<EdgePair>* edges = new vector<EdgePair>;
+
+ // Get all edges to the left of origin_sample
+ get_subsampled_edges(*edges, 0, origin_sample, min_length, sig_index, false);
+
+ // If we don't specify "first only", the first and last edge are the states
+ // at samples 0 and origin_sample. If only those exist, there are no edges
+ if (edges->size() == 2) {
+ delete edges;
+ return;
+ }
+
+ // Dismiss the entry for origin_sample so that back() gives us the
+ // real last entry
+ edges->pop_back();
+ dest.push_back(edges->back());
+ edges->clear();
+
+ // Get first edge to the right of origin_sample
+ get_subsampled_edges(*edges, origin_sample, sample_count_, min_length, sig_index, true);
+
+ // "first only" is specified, so nothing needs to be dismissed
+ if (edges->size() == 0) {
+ delete edges;
+ return;
+ }
+
+ dest.push_back(edges->front());
+
+ delete edges;
+}
+
+void LogicSegment::reallocate_mipmap_level(MipMapLevel &m)
+{
+ lock_guard<recursive_mutex> lock(mutex_);
+
+ const uint64_t new_data_length = ((m.length + MipMapDataUnit - 1) /
+ MipMapDataUnit) * MipMapDataUnit;
+
+ if (new_data_length > m.data_length) {
+ m.data_length = new_data_length;
+
+ // Padding is added to allow for the uint64_t write word
+ m.data = realloc(m.data, new_data_length * unit_size_ +
+ sizeof(uint64_t));
+ }
+}
+
+void LogicSegment::append_payload_to_mipmap()
+{
+ MipMapLevel &m0 = mip_map_[0];
+ uint64_t prev_length;
+ uint8_t *dest_ptr;
+ SegmentDataIterator* it;
+ uint64_t accumulator;
+ unsigned int diff_counter;
+
+ // Expand the data buffer to fit the new samples
+ prev_length = m0.length;
+ m0.length = sample_count_ / MipMapScaleFactor;
+
+ // Break off if there are no new samples to compute
+ if (m0.length == prev_length)
+ return;
+
+ reallocate_mipmap_level(m0);
+
+ dest_ptr = (uint8_t*)m0.data + prev_length * unit_size_;
+
+ // Iterate through the samples to populate the first level mipmap
+ const uint64_t start_sample = prev_length * MipMapScaleFactor;
+ const uint64_t end_sample = m0.length * MipMapScaleFactor;
+ uint64_t len_sample = end_sample - start_sample;
+ it = begin_sample_iteration(start_sample);
+ while (len_sample > 0) {
+ // Number of samples available in this chunk
+ uint64_t count = get_iterator_valid_length(it);
+ // Reduce if less than asked for
+ count = std::min(count, len_sample);
+ uint8_t *src_ptr = get_iterator_value(it);
+ // Submit these contiguous samples to downsampling in bulk
+ if (unit_size_ == 1)
+ downsampleT<uint8_t>(src_ptr, dest_ptr, count);
+ else if (unit_size_ == 2)
+ downsampleT<uint16_t>(src_ptr, dest_ptr, count);
+ else if (unit_size_ == 4)
+ downsampleT<uint32_t>(src_ptr, dest_ptr, count);
+ else if (unit_size_ == 8)
+ downsampleT<uint8_t>(src_ptr, dest_ptr, count);
+ else
+ downsampleGeneric(src_ptr, dest_ptr, count);
+ len_sample -= count;
+ // Advance iterator, should move to start of next chunk
+ continue_sample_iteration(it, count);
+ }
+ end_sample_iteration(it);
+
+ // Compute higher level mipmaps
+ for (unsigned int level = 1; level < ScaleStepCount; level++) {
+ MipMapLevel &m = mip_map_[level];
+ const MipMapLevel &ml = mip_map_[level - 1];
+
+ // Expand the data buffer to fit the new samples
+ prev_length = m.length;
+ m.length = ml.length / MipMapScaleFactor;
+
+ // Break off if there are no more samples to be computed
+ if (m.length == prev_length)
+ break;
+
+ reallocate_mipmap_level(m);
+
+ // Subsample the lower level
+ const uint8_t* src_ptr = (uint8_t*)ml.data +
+ unit_size_ * prev_length * MipMapScaleFactor;
+ const uint8_t *const end_dest_ptr =
+ (uint8_t*)m.data + unit_size_ * m.length;
+
+ for (dest_ptr = (uint8_t*)m.data +
+ unit_size_ * prev_length;
+ dest_ptr < end_dest_ptr;
+ dest_ptr += unit_size_) {
+ accumulator = 0;
+ diff_counter = MipMapScaleFactor;
+ while (diff_counter-- > 0) {
+ accumulator |= unpack_sample(src_ptr);
+ src_ptr += unit_size_;
+ }
+
+ pack_sample(dest_ptr, accumulator);
+ }
+ }
+}
+
+uint64_t LogicSegment::get_unpacked_sample(uint64_t index) const
+{
+ assert(index < sample_count_);
+
+ assert(unit_size_ <= 8); // 8 * 8 = 64 channels
+ uint8_t data[8];
+
+ get_raw_samples(index, 1, data);
+
+ return unpack_sample(data);