summaryrefslogtreecommitdiffstats
path: root/opengl/shared/OpenglCodecCommon/ChecksumCalculator.h
blob: 4cc231a7d36b479b64460cd71364492e4640f966 (plain)
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <stdint.h>
#include <stdlib.h>

// Set TRACE_CHECKSUMHELPER to 1 to debug creation/destruction of GLprotocol
// instances.
#define TRACE_CHECKSUMHELPER 0

#if TRACE_CHECKSUMHELPER
#define LOG_CHECKSUMHELPER(x...) fprintf(stderr, x)
#else
#define LOG_CHECKSUMHELPER(x...)
#endif

// ChecksumCalculator adds checksum as an array of bytes to GL pipe communication, which
// size depends on the protocol version. Each pipe should use one ChecksumCalculator.
// It can:
//      (1) take a list of buffers one by one and compute their checksum string,
//          in this case the checksum should be as the data in those buffers are
//          concatenated;
//      (2) compute the checksum of the buffer list, then either write them into
//          a buffer provided by user, or compare it against a checksum provided
//          by user
//      (3) support different checksum version in future.
//
// For backward compatibility, checksum version 0 behaves the same as there is
// no checksum (i.e., checksumByteSize returns 0, validate always returns true,
// addBuffer and writeCheckSum does nothing).
//
// Notice that to detect package lost, ChecksumCalculator also keeps track of how
// many times it generates/validates checksums, and might use it as part of the
// checksum.
//
// To evaluate checksums from a list of data buffers buf1, buf2... Please call
// addBuffer(buf1, buf1len), addBuffer(buf2, buf2len) ... in order.
// Then if the checksum needs to be encoded into a buffer, one needs to allocate
// a checksum buffer with size checksumByteSize(), and call
// writeChecksum(checksumBuffer) to write the checksum to the buffer.
// If the checksum needs to be validated against an existing one, one needs to
// call validate(existChecksum, existChecksumLen).
//
// The checksum generator and validator must be set to the same version, and
// the validator must check ALL checksums in the order they are generated,
// otherwise the validation function will return false.
//
// It is allowed to change the checksum version between calculating two
// checksums. This is designed for backward compatibility reason.
//
// Example 1, encoding and decoding:
//
// bool testChecksum(void* buf, size_t bufLen) {
//     // encoding message
//     ChecksumCalculator encoder;
//     encoder.setVersion(1);
//     encoder.addBuffer(buf, bufLen);
//     std::vector<unsigned char> message(bufLen + encoder.checksumByteSize());
//     memcpy(&message[0], buf, bufLen);
//     encoder.writeChecksum(&message[0] + bufLen, encoder.checksumByteSize());
//
//     // decoding message
//     ChecksumCalculator decoder;
//     decoder.setVersion(1);
//     decoder.addBuffer(&message[0], bufLen);
//     return decoder.validate(&message[0] + bufLen, decoder.checksumByteSize());
// }
// The return value is true.
//
// Example 2, decoding will fail if the order of messages is wrong:
//
// bool testChecksumOrder(void* buf1, size_t bufLen1,
//                        void* buf2, size_t bufLen2) {
//     // encoding messages
//     ChecksumCalculator encoder;
//     encoder.setVersion(1);
//
//     std::vector<unsigned char> message1(bufLen1 + encoder.checksumByteSize());
//     std::vector<unsigned char> message2(bufLen2 + encoder.checksumByteSize());
//
//     encoder.addBuffer(buf1, bufLen1);
//     std::vector<unsigned char> message1(bufLen1 + encoder.checksumByteSize());
//     memcpy(&message1[0], buf1, bufLen1);
//     encoder.writeChecksum(&message1[0] + bufLen1, encoder.checksumByteSize());
//
//     encoder.addBuffer(buf2, bufLen2);
//     std::vector<unsigned char> message2(bufLen2 + encoder.checksumByteSize());
//     memcpy(&message2[0], buf2, bufLen2);
//     encoder.writeChecksum(&message2[0] + bufLen2, encoder.checksumByteSize());
//
//     // decoding messages
//     ChecksumCalculator decoder;
//     decoder.setVersion(1);
//     decoder.addBuffer(&message2[0], bufLen2);
//     // returns false because the decoding order is not consistent with
//     // encoding order
//     if (!decoder.validate(&message2[0]+bufLen2, decoder.checksumByteSize())) {
//         return false;
//     }
//
//     decoder.addBuffer(&message1[0], bufLen1);
//     if (!decoder.validate(&message1[0]+bufLen1, decoder.checksumByteSize())) {
//         return false;
//     }
//
//     return false;
// }

class ChecksumCalculator {
public:
    // Get and set current checksum version
    uint32_t getVersion() const { return m_version; }
    // Call setVersion to set a checksum version. It should be called before
    // addBuffer(), writeChecksum() and validate(). And it should be called
    // exact once per rendering thread if both host and guest support checksum.
    // It won't be called if either host or guest does not support checksum.
    bool setVersion(uint32_t version);

    // Maximum supported checksum version
    static uint32_t getMaxVersion();
    // A version string that looks like "ANDROID_EMU_CHECKSUM_HELPER_v1"
    // Used multiple times when the guest queries the maximum supported version
    // from the host.
    // The library owns the returned pointer. The returned pointer will be
    // deconstructed when unloading library.
    static const char* getMaxVersionStr();
    static const char* getMaxVersionStrPrefix();

    // Size of checksum in the current version
    size_t checksumByteSize() const;

    // Update the current checksum value from the data
    // at |buf| of |bufLen| bytes. Once all buffers
    // have been added, call writeChecksum() to store
    // the final checksum value and reset its state.
    void addBuffer(const void* buf, size_t bufLen);
    // Write the checksum from the list of buffers to outputChecksum
    // Will reset the list of buffers by calling resetChecksum.
    // Return false if the buffer is not long enough
    // Please query buffer size from checksumByteSize()
    bool writeChecksum(void* outputChecksum, size_t outputChecksumLen);
    // Restore the states for computing checksums.
    // Automatically called at the end of writeChecksum and validate.
    // Can also be used to abandon the current checksum being calculated.
    // Notes: it doesn't update the internal read / write counter
    void resetChecksum();

    // Calculate the checksum from the list of buffers and
    // compare it with the checksum encoded in expectedChecksum
    // Will reset the list of buffers by calling resetChecksum.
    bool validate(const void* expectedChecksum, size_t expectedChecksumLen);
protected:
    uint32_t m_version = 0;
    // A temporary state used to compute the total length of a list of buffers,
    // if addBuffer is called.
    uint32_t m_numRead = 0;
    uint32_t m_numWrite = 0;
    // m_isEncodingChecksum is true when between addBuffer and writeChecksum
    bool m_isEncodingChecksum = false;
private:
    // Compute a 32bit checksum
    // Used in protocol v1
    uint32_t computeV1Checksum();
    // The buffer used in protocol version 1 to compute checksum.
    uint32_t m_v1BufferTotalLength = 0;
};