Merge field updates into a msgpack map without full decode. Takes a base msgpack map and a list of `(field_name, raw_msgpack_value)` updates. Returns a new msgpack map with updated fields replaced and new fields appended. Fields not in `updates` are copied from the original.
(base: &[u8], updates: &[(&str, &[u8])])
| 194 | /// Returns a new msgpack map with updated fields replaced and new fields appended. |
| 195 | /// Fields not in `updates` are copied from the original. |
| 196 | pub fn merge_fields(base: &[u8], updates: &[(&str, &[u8])]) -> Vec<u8> { |
| 197 | use std::collections::HashSet; |
| 198 | |
| 199 | let update_names: HashSet<&str> = updates.iter().map(|(k, _)| *k).collect(); |
| 200 | |
| 201 | let (count, body_start) = match crate::msgpack_scan::reader::map_header(base, 0) { |
| 202 | Some(v) => v, |
| 203 | None => { |
| 204 | // Not a valid map — build from updates only. |
| 205 | let mut buf = Vec::with_capacity(updates.len() * 32); |
| 206 | write_map_header(&mut buf, updates.len()); |
| 207 | for (k, v) in updates { |
| 208 | write_str(&mut buf, k); |
| 209 | buf.extend_from_slice(v); |
| 210 | } |
| 211 | return buf; |
| 212 | } |
| 213 | }; |
| 214 | |
| 215 | // Count fields: existing (not overwritten) + updates. |
| 216 | let mut kept = 0usize; |
| 217 | let mut pos = body_start; |
| 218 | for _ in 0..count { |
| 219 | let key = crate::msgpack_scan::reader::read_str(base, pos); |
| 220 | pos = match crate::msgpack_scan::reader::skip_value(base, pos) { |
| 221 | Some(p) => p, |
| 222 | None => break, |
| 223 | }; |
| 224 | pos = match crate::msgpack_scan::reader::skip_value(base, pos) { |
| 225 | Some(p) => p, |
| 226 | None => break, |
| 227 | }; |
| 228 | if let Some(k) = &key { |
| 229 | if !update_names.contains(&k[..]) { |
| 230 | kept += 1; |
| 231 | } |
| 232 | } else { |
| 233 | kept += 1; |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | let new_count = kept + updates.len(); |
| 238 | let mut buf = Vec::with_capacity( |
| 239 | base.len() |
| 240 | + updates |
| 241 | .iter() |
| 242 | .map(|(k, v)| k.len() + v.len() + 4) |
| 243 | .sum::<usize>(), |
| 244 | ); |
| 245 | write_map_header(&mut buf, new_count); |
| 246 | |
| 247 | // Copy non-overwritten fields from base. |
| 248 | pos = body_start; |
| 249 | for _ in 0..count { |
| 250 | let key_start = pos; |
| 251 | let key = crate::msgpack_scan::reader::read_str(base, pos); |
| 252 | pos = match crate::msgpack_scan::reader::skip_value(base, pos) { |
| 253 | Some(p) => p, |
no test coverage detected