TileIndex Channel::split_tile_if_needed(TileIndex ti, Tile &tile) { TileIndex new_root_index = TileIndex::null(); if (tile.binary_length() <= m_max_tile_size) return new_root_index; Tile children[2]; TileIndex child_indexes[2]; if (verbosity) log_f("split_tile_if_needed: splitting tile %s", ti.to_string().c_str()); // If we're splitting an "all" tile, it means that until now the channel has only had one tile's worth of // data, and that a proper root tile location couldn't be selected. Select a new root tile now. if (ti.is_nonnegative_all()) { // TODO: this breaks if all samples are at one time ti = new_root_index = TileIndex::index_containing(Range(tile.first_sample_time(), tile.last_sample_time())); if (verbosity) log_f("split_tile_if_needed: Moving root tile to %s", ti.to_string().c_str()); } child_indexes[0]= ti.left_child(); child_indexes[1]= ti.right_child(); double split_time = ti.right_child().start_time(); split_samples(tile.double_samples, split_time, children[0], children[1]); split_samples(tile.string_samples, split_time, children[0], children[1]); for (int i = 0; i < 2; i++) { assert(!has_tile(child_indexes[i])); assert(split_tile_if_needed(child_indexes[i], children[i]) == TileIndex::null()); write_tile(child_indexes[i], children[i]); } create_parent_tile_from_children(ti, tile, children); return new_root_index; }
TileIndex Channel::find_child_overlapping_time(TileIndex ti, double t, int desired_level) const { assert(!ti.is_null()); // Start at root tile and move downwards while (ti.level > desired_level) { // Select correct child TileIndex child = t < ti.left_child().end_time() ? ti.left_child() : ti.right_child(); if (child.is_null() || !tile_exists(child)) break; ti = child; } return ti; }
void Channel::create_parent_tile_from_children(TileIndex parent_index, Tile &parent, Tile children[]) { // Subsample the children to create the parent // when do we want to show original values? // when do we want to do a real low-pass filter? // do we need to filter more than just the child tiles? e.g. gaussian beyond the tile border if (verbosity) log_f("Channel: creating parent %s from children %s, %s", parent_index.to_string().c_str(), parent_index.left_child().to_string().c_str(), parent_index.right_child().to_string().c_str()); combine_samples(BT_CHANNEL_DOUBLE_SAMPLES, parent_index, parent.double_samples, children[0].double_samples, children[1].double_samples); if (children[0].double_samples.size() + children[1].double_samples.size()) assert(parent.double_samples.size()); combine_samples(BT_CHANNEL_STRING_SAMPLES, parent_index, parent.string_samples, children[0].string_samples, children[1].string_samples); if (children[0].string_samples.size() + children[1].string_samples.size()) assert(parent.string_samples.size()); parent.ranges = children[0].ranges; parent.ranges.add(children[1].ranges); }
std::string Channel::dump_tile_summaries_internal(TileIndex ti, int level) const { Tile tile; if (!read_tile(ti, tile)) return ""; std::string ret = string_printf("%*s%d.%d: %zd samples\n", level, "", ti.level, ti.offset, tile.double_samples.size()); return ret + dump_tile_summaries_internal(ti.left_child(), level+1) + dump_tile_summaries_internal(ti.right_child(), level+1); }
bool Channel::read_tile_or_closest_ancestor(TileIndex ti, TileIndex &ret_index, Tile &ret) const { Locker lock(*this); // Lock self and hold lock until exiting this method ChannelInfo info; bool success = read_info(info); if (!success) { if (verbosity) log_f("read_tile_or_closest_ancestor: can't read info"); return false; } TileIndex root = info.nonnegative_root_tile_index; if (ti.is_ancestor_of(root)) { ret_index = root; } else { if (ti != root && !root.is_ancestor_of(ti)) { // Tile isn't under root return false; } assert(tile_exists(root)); ret_index = root; while (ret_index != ti) { TileIndex child = ti.start_time() < ret_index.left_child().end_time() ? ret_index.left_child() : ret_index.right_child(); if (!tile_exists(child)) break; ret_index = child; } } // ret_index now holds closest ancestor to ti (or ti itself if it exists) assert(read_tile(ret_index, ret)); return true; }
void Channel::add_data_internal(const std::vector<DataSample<T> > &data, DataRanges *channel_ranges) { if (!data.size()) return; // Sanity check if (data[0].time < 0) throw std::runtime_error("Unimplemented feature: adding data with negative time"); for (unsigned i = 0; i < data.size()-1; i++) { if (data[i].time > data[i+1].time) throw std::runtime_error("Attempt to add data that is not sorted by ascending time"); } // regenerate = empty set Locker lock(*this); // Lock self and hold lock until exiting this method std::set<TileIndex> to_regenerate; ChannelInfo info; bool success = read_info(info); if (!success) { // New channel info.magic = ChannelInfo::MAGIC; info.version = 0x00010000; info.times = Range(data[0].time, data.back().time); info.nonnegative_root_tile_index = TileIndex::nonnegative_all(); create_tile(TileIndex::nonnegative_all()); info.negative_root_tile_index = TileIndex::null(); } else { info.times.add(Range(data[0].time, data.back().time)); // If we're not the all-tile, see if we need to move the root upwards if (info.nonnegative_root_tile_index != TileIndex::nonnegative_all()) { TileIndex new_nonnegative_root_tile_index = TileIndex::index_containing(info.times); if (new_nonnegative_root_tile_index.level > info.nonnegative_root_tile_index.level) { // Root index changed. Confirm new root is parent or more distant ancestor of old root assert(new_nonnegative_root_tile_index.is_ancestor_of(info.nonnegative_root_tile_index)); // Trigger regeneration from old root's parent, up through new root to_regenerate.insert(info.nonnegative_root_tile_index.parent()); move_root_upwards(new_nonnegative_root_tile_index, info.nonnegative_root_tile_index); info.nonnegative_root_tile_index = new_nonnegative_root_tile_index; } } } unsigned i=0; while (i < data.size()) { TileIndex ti= find_lowest_child_overlapping_time(info.nonnegative_root_tile_index, data[i].time); assert(!ti.is_null()); Tile tile; assert(read_tile(ti, tile)); const DataSample<T> *begin = &data[i]; while (i < data.size() && ti.contains_time(data[i].time)) i++; const DataSample<T> *end = &data[i]; tile.insert_samples(begin, end); TileIndex new_root = split_tile_if_needed(ti, tile); if (new_root != TileIndex::null()) { assert(ti == TileIndex::nonnegative_all()); if (verbosity) log_f("Channel: %s changing root from %s to %s", descriptor().c_str(), ti.to_string().c_str(), new_root.to_string().c_str()); info.nonnegative_root_tile_index = new_root; delete_tile(ti); // Delete old root ti = new_root; } write_tile(ti, tile); if (ti == info.nonnegative_root_tile_index && channel_ranges) { *channel_ranges = tile.ranges; } if (ti != info.nonnegative_root_tile_index) to_regenerate.insert(ti.parent()); } // Regenerate from lowest level to highest while (!to_regenerate.empty()) { TileIndex ti = *to_regenerate.begin(); to_regenerate.erase(to_regenerate.begin()); Tile regenerated, children[2]; assert(read_tile(ti.left_child(), children[0])); assert(read_tile(ti.right_child(), children[1])); create_parent_tile_from_children(ti, regenerated, children); write_tile(ti, regenerated); if (ti == info.nonnegative_root_tile_index && channel_ranges) { *channel_ranges = regenerated.ranges; } if (ti != info.nonnegative_root_tile_index) to_regenerate.insert(ti.parent()); } write_info(info); }