summaryrefslogtreecommitdiffstats
path: root/src/network/packet.h
blob: 6d84cfbac02234070c78cf593c98afe2acdc428e (plain) (blame)
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
182
183
184
185
186
187
188
189
190
191
192
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <array>
#include <vector>
#include "common/common_types.h"

namespace Network {

/// A class for serialize data for network transfer. It also handles endianess
class Packet {
    /// A bool-like type that cannot be converted to integer or pointer types
    typedef bool (Packet::*BoolType)(std::size_t);

public:
    Packet();
    ~Packet();

    /**
     * Append data to the end of the packet
     * @param data        Pointer to the sequence of bytes to append
     * @param size_in_bytes Number of bytes to append
     */
    void Append(const void* data, std::size_t size_in_bytes);

    /**
     * Reads data from the current read position of the packet
     * @param out_data        Pointer where the data should get written to
     * @param size_in_bytes Number of bytes to read
     */
    void Read(void* out_data, std::size_t size_in_bytes);

    /**
     * Clear the packet
     * After calling Clear, the packet is empty.
     */
    void Clear();

    /**
     * Ignores bytes while reading
     * @param length THe number of bytes to ignore
     */
    void IgnoreBytes(u32 length);

    /**
     * Get a pointer to the data contained in the packet
     * @return Pointer to the data
     */
    const void* GetData() const;

    /**
     * This function returns the number of bytes pointed to by
     * what getData returns.
     * @return Data size, in bytes
     */
    std::size_t GetDataSize() const;

    /**
     * This function is useful to know if there is some data
     * left to be read, without actually reading it.
     * @return True if all data was read, false otherwise
     */
    bool EndOfPacket() const;
    /**
     * Test the validity of the packet, for reading
     * This operator allows to test the packet as a boolean
     * variable, to check if a reading operation was successful.
     *
     * A packet will be in an invalid state if it has no more
     * data to read.
     *
     * This behaviour is the same as standard C++ streams.
     *
     * Usage example:
     * @code
     * float x;
     * packet >> x;
     * if (packet)
     * {
     *    // ok, x was extracted successfully
     * }
     *
     * // -- or --
     *
     * float x;
     * if (packet >> x)
     * {
     *    // ok, x was extracted successfully
     * }
     * @endcode
     *
     * Don't focus on the return type, it's equivalent to bool but
     * it disallows unwanted implicit conversions to integer or
     * pointer types.
     *
     * @return True if last data extraction from packet was successful
     */
    operator BoolType() const;

    /// Overloads of operator >> to read data from the packet
    Packet& operator>>(bool& out_data);
    Packet& operator>>(s8& out_data);
    Packet& operator>>(u8& out_data);
    Packet& operator>>(s16& out_data);
    Packet& operator>>(u16& out_data);
    Packet& operator>>(s32& out_data);
    Packet& operator>>(u32& out_data);
    Packet& operator>>(float& out_data);
    Packet& operator>>(double& out_data);
    Packet& operator>>(char* out_data);
    Packet& operator>>(std::string& out_data);
    template <typename T>
    Packet& operator>>(std::vector<T>& out_data);
    template <typename T, std::size_t S>
    Packet& operator>>(std::array<T, S>& out_data);

    /// Overloads of operator << to write data into the packet
    Packet& operator<<(bool in_data);
    Packet& operator<<(s8 in_data);
    Packet& operator<<(u8 in_data);
    Packet& operator<<(s16 in_data);
    Packet& operator<<(u16 in_data);
    Packet& operator<<(s32 in_data);
    Packet& operator<<(u32 in_data);
    Packet& operator<<(float in_data);
    Packet& operator<<(double in_data);
    Packet& operator<<(const char* in_data);
    Packet& operator<<(const std::string& in_data);
    template <typename T>
    Packet& operator<<(const std::vector<T>& in_data);
    template <typename T, std::size_t S>
    Packet& operator<<(const std::array<T, S>& data);

private:
    /// Disallow comparisons between packets
    bool operator==(const Packet& right) const;
    bool operator!=(const Packet& right) const;

    /**
     * Check if the packet can extract a given number of bytes
     * This function updates accordingly the state of the packet.
     * @param size Size to check
     * @return True if size bytes can be read from the packet
     */
    bool CheckSize(std::size_t size);

    // Member data
    std::vector<char> data; ///< Data stored in the packet
    std::size_t read_pos;   ///< Current reading position in the packet
    bool is_valid;          ///< Reading state of the packet
};

template <typename T>
Packet& Packet::operator>>(std::vector<T>& out_data) {
    for (u32 i = 0; i < out_data.size(); ++i) {
        T character = 0;
        *this >> character;
        out_data[i] = character;
    }
    return *this;
}

template <typename T, std::size_t S>
Packet& Packet::operator>>(std::array<T, S>& out_data) {
    for (u32 i = 0; i < out_data.size(); ++i) {
        T character = 0;
        *this >> character;
        out_data[i] = character;
    }
    return *this;
}

template <typename T>
Packet& Packet::operator<<(const std::vector<T>& in_data) {
    for (u32 i = 0; i < in_data.size(); ++i) {
        *this << in_data[i];
    }
    return *this;
}

template <typename T, std::size_t S>
Packet& Packet::operator<<(const std::array<T, S>& in_data) {
    for (u32 i = 0; i < in_data.size(); ++i) {
        *this << in_data[i];
    }
    return *this;
}

} // namespace Network