1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use super::ByteRange;
use crate::shared::{compress_brotli, decompress_brotli, Blob};
use byteorder::{BigEndian as BE, ReadBytesExt, WriteBytesExt};
use std::{io::Cursor, ops::Div};

const TILE_INDEX_LENGTH: usize = 12;

#[derive(Debug, PartialEq, Eq)]
pub struct TileIndex {
	index: Vec<ByteRange>,
}

unsafe impl Send for TileIndex {}

impl TileIndex {
	pub fn new_empty(count: usize) -> Self {
		let index = vec![ByteRange::new(0, 0); count];
		Self { index }
	}

	pub fn from_blob(buf: Blob) -> Self {
		let count = buf.len().div(TILE_INDEX_LENGTH);
		assert_eq!(
			count * TILE_INDEX_LENGTH,
			buf.len(),
			"Tile index is defective: buffer length is not a multiple of {}",
			TILE_INDEX_LENGTH
		);

		let mut index = vec![ByteRange::new(0, 0); count];
		let mut cursor = Cursor::new(buf.as_slice());
		for item in &mut index {
			item.offset = cursor.read_u64::<BE>().unwrap();
			item.length = cursor.read_u32::<BE>().unwrap() as u64;
		}

		Self { index }
	}

	pub fn from_brotli_blob(buf: Blob) -> Self {
		Self::from_blob(decompress_brotli(buf).unwrap())
	}

	pub fn set(&mut self, index: usize, tile_byte_range: ByteRange) {
		self.index[index] = tile_byte_range;
	}

	pub fn as_blob(&self) -> Blob {
		let mut buf = Vec::new();
		let mut cursor = Cursor::new(&mut buf);
		for range in &self.index {
			cursor.write_u64::<BE>(range.offset).unwrap();
			cursor.write_u32::<BE>(range.length as u32).unwrap();
		}

		Blob::from(buf)
	}

	pub fn as_brotli_blob(&self) -> Blob {
		compress_brotli(self.as_blob()).unwrap()
	}

	pub fn get(&self, index: usize) -> &ByteRange {
		&self.index[index]
	}

	pub fn len(&self) -> usize {
		self.index.len()
	}

	pub fn iter(&self) -> impl Iterator<Item = &ByteRange> {
		self.index.iter()
	}

	pub fn add_offset(&mut self, offset: u64) {
		self.index.iter_mut().for_each(|r| r.offset += offset);
	}
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn init() {
		const COUNT: u64 = 16;

		let mut index = TileIndex::new_empty(COUNT as usize);
		assert_eq!(index.len(), COUNT as usize);

		for i in 0..COUNT {
			index.set(i as usize, ByteRange::new(i * i, i));
			assert_eq!(index.get(i as usize), &ByteRange::new(i * i, i));
		}

		index.add_offset(18);

		for (index, range) in index.iter().enumerate() {
			let i = index as u64;
			assert_eq!(range, &ByteRange::new(i * i + 18, i));
		}
	}

	#[test]
	fn conversion() {
		let mut index1 = TileIndex::new_empty(100);
		for i in 0..100u64 {
			index1.set(i as usize, ByteRange::new(i * 1000, i * 2000));
		}
		let index2 = TileIndex::from_brotli_blob(index1.as_brotli_blob());
		assert_eq!(index1, index2);
	}
}