1 // Copyright (C) 2016  Lukas Lalinsky
2 // Distributed under the MIT license, see the LICENSE file for details.
3 
4 #include "fingerprint_decompressor.h"
5 #include "debug.h"
6 #include "utils/pack_int3_array.h"
7 #include "utils/pack_int5_array.h"
8 #include "utils/unpack_int3_array.h"
9 #include "utils/unpack_int5_array.h"
10 #include "utils.h"
11 
12 namespace chromaprint {
13 
14 static const int kMaxNormalValue = 7;
15 static const int kNormalBits = 3;
16 static const int kExceptionBits = 5;
17 
FingerprintDecompressor()18 FingerprintDecompressor::FingerprintDecompressor()
19 {
20 }
21 
UnpackBits()22 void FingerprintDecompressor::UnpackBits()
23 {
24 	int i = 0, last_bit = 0, value = 0;
25 	for (size_t j = 0; j < m_bits.size(); j++) {
26 		int bit = m_bits[j];
27 		if (bit == 0) {
28 			m_output[i] = (i > 0) ? value ^ m_output[i - 1] : value;
29 			value = 0;
30 			last_bit = 0;
31 			i++;
32 			continue;
33 		}
34 		bit += last_bit;
35 		last_bit = bit;
36 		value |= 1 << (bit - 1);
37 	}
38 }
39 
Decompress(const std::string & input)40 bool FingerprintDecompressor::Decompress(const std::string &input)
41 {
42 	if (input.size() < 4) {
43 		DEBUG("FingerprintDecompressor::Decompress() -- Invalid fingerprint (shorter than 4 bytes)");
44 		return false;
45 	}
46 
47 	m_algorithm = input[0];
48 
49 	const size_t num_values =
50 		((size_t)((unsigned char)(input[1])) << 16) |
51 		((size_t)((unsigned char)(input[2])) <<  8) |
52 		((size_t)((unsigned char)(input[3]))      );
53 
54 	size_t offset = 4;
55 	m_bits.resize(GetUnpackedInt3ArraySize(input.size() - offset));
56 	UnpackInt3Array(input.begin() + offset, input.end(), m_bits.begin());
57 
58 	size_t found_values = 0, num_exceptional_bits = 0;
59 	for (size_t i = 0; i < m_bits.size(); i++) {
60 		if (m_bits[i] == 0) {
61 			found_values += 1;
62 			if (found_values == num_values) {
63 				m_bits.resize(i + 1);
64 				break;
65 			}
66 		} else if (m_bits[i] == kMaxNormalValue) {
67 			num_exceptional_bits += 1;
68 		}
69 	}
70 
71 	if (found_values != num_values) {
72 		DEBUG("FingerprintDecompressor::Decompress() -- Invalid fingerprint (too short, not enough input for normal bits)");
73 		return false;
74 	}
75 
76 	offset += GetPackedInt3ArraySize(m_bits.size());
77 	if (input.size() < offset + GetPackedInt5ArraySize(num_exceptional_bits)) {
78 		DEBUG("FingerprintDecompressor::Decompress() -- Invalid fingerprint (too short, not enough input for exceptional bits)");
79 		return false;
80 	}
81 
82 	if (num_exceptional_bits) {
83 		m_exceptional_bits.resize(GetUnpackedInt5ArraySize(GetPackedInt5ArraySize(num_exceptional_bits)));
84 		UnpackInt5Array(input.begin() + offset, input.end(), m_exceptional_bits.begin());
85 		for (size_t i = 0, j = 0; i < m_bits.size(); i++) {
86 			if (m_bits[i] == kMaxNormalValue) {
87 				m_bits[i] += m_exceptional_bits[j++];
88 			}
89 		}
90 	}
91 
92 	m_output.assign(num_values, -1);
93 
94 	UnpackBits();
95 	return true;
96 }
97 
98 }; // namespace chromaprint
99