1/*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef IMG_UTILS_ENDIAN_UTILS
18#define IMG_UTILS_ENDIAN_UTILS
19
20#include <img_utils/Output.h>
21
22#include <cutils/compiler.h>
23#include <utils/Errors.h>
24#include <stdint.h>
25#include <endian.h>
26#include <assert.h>
27
28namespace android {
29namespace img_utils {
30
31/**
32 * Endianness types supported.
33 */
34enum ANDROID_API Endianness {
35    UNDEFINED_ENDIAN, // Default endianness will be used.
36    BIG,
37    LITTLE
38};
39
40/**
41 * Convert from the native device endianness to big endian.
42 */
43template<typename T>
44T convertToBigEndian(T in);
45
46/**
47 * Convert from the native device endianness to little endian.
48 */
49template<typename T>
50T convertToLittleEndian(T in);
51
52/**
53 * A utility class for writing to an Output with the given endianness.
54 */
55class ANDROID_API EndianOutput : public Output {
56    public:
57        /**
58         * Wrap the given Output.  Calling write methods will result in
59         * writes to this output.
60         */
61        EndianOutput(Output* out, Endianness end=LITTLE);
62
63        virtual ~EndianOutput();
64
65        /**
66         * Call open on the wrapped output.
67         */
68        virtual status_t open();
69
70        /**
71         * Call close on the wrapped output.
72         */
73        virtual status_t close();
74
75        /**
76         * Set the endianness to use when writing.
77         */
78        virtual void setEndianness(Endianness end);
79
80        /**
81         * Get the currently configured endianness.
82         */
83        virtual Endianness getEndianness() const;
84
85        /**
86         * Get the current number of bytes written by this EndianOutput.
87         */
88        virtual uint32_t getCurrentOffset() const;
89
90
91        // TODO: switch write methods to uint32_t instead of size_t,
92        // the max size of a TIFF files is bounded
93
94        /**
95         * The following methods will write elements from given input buffer to the output.
96         * Count elements in the buffer will be written with the endianness set for this
97         * EndianOutput.  If the given offset is greater than zero, that many elements will
98         * be skipped in the buffer before writing.
99         *
100         * Returns OK on success, or a negative error code.
101         */
102        virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
103
104        virtual status_t write(const int8_t* buf, size_t offset, size_t count);
105
106        virtual status_t write(const uint16_t* buf, size_t offset, size_t count);
107
108        virtual status_t write(const int16_t* buf, size_t offset, size_t count);
109
110        virtual status_t write(const uint32_t* buf, size_t offset, size_t count);
111
112        virtual status_t write(const int32_t* buf, size_t offset, size_t count);
113
114        virtual status_t write(const uint64_t* buf, size_t offset, size_t count);
115
116        virtual status_t write(const int64_t* buf, size_t offset, size_t count);
117
118        virtual status_t write(const float* buf, size_t offset, size_t count);
119
120        virtual status_t write(const double* buf, size_t offset, size_t count);
121
122    protected:
123        template<typename T>
124        inline status_t writeHelper(const T* buf, size_t offset, size_t count);
125
126        uint32_t mOffset;
127        Output* mOutput;
128        Endianness mEndian;
129};
130
131template<typename T>
132inline status_t EndianOutput::writeHelper(const T* buf, size_t offset, size_t count) {
133    assert(offset <= count);
134    status_t res = OK;
135    size_t size = sizeof(T);
136    switch(mEndian) {
137        case BIG: {
138            for (size_t i = offset; i < count; ++i) {
139                T tmp = convertToBigEndian<T>(buf[offset + i]);
140                if ((res = mOutput->write(reinterpret_cast<uint8_t*>(&tmp), 0, size))
141                        != OK) {
142                    return res;
143                }
144                mOffset += size;
145            }
146            break;
147        }
148        case LITTLE: {
149            for (size_t i = offset; i < count; ++i) {
150                T tmp = convertToLittleEndian<T>(buf[offset + i]);
151                if ((res = mOutput->write(reinterpret_cast<uint8_t*>(&tmp), 0, size))
152                        != OK) {
153                    return res;
154                }
155                mOffset += size;
156            }
157            break;
158        }
159        default: {
160            return BAD_VALUE;
161        }
162    }
163    return res;
164}
165
166template<>
167inline uint8_t convertToBigEndian(uint8_t in) {
168    return in;
169}
170
171template<>
172inline int8_t convertToBigEndian(int8_t in) {
173    return in;
174}
175
176template<>
177inline uint16_t convertToBigEndian(uint16_t in) {
178    return htobe16(in);
179}
180
181template<>
182inline int16_t convertToBigEndian(int16_t in) {
183    return htobe16(in);
184}
185
186template<>
187inline uint32_t convertToBigEndian(uint32_t in) {
188    return htobe32(in);
189}
190
191template<>
192inline int32_t convertToBigEndian(int32_t in) {
193    return htobe32(in);
194}
195
196template<>
197inline uint64_t convertToBigEndian(uint64_t in) {
198    return htobe64(in);
199}
200
201template<>
202inline int64_t convertToBigEndian(int64_t in) {
203    return htobe64(in);
204}
205
206template<>
207inline uint8_t convertToLittleEndian(uint8_t in) {
208    return in;
209}
210
211template<>
212inline int8_t convertToLittleEndian(int8_t in) {
213    return in;
214}
215
216template<>
217inline uint16_t convertToLittleEndian(uint16_t in) {
218    return htole16(in);
219}
220
221template<>
222inline int16_t convertToLittleEndian(int16_t in) {
223    return htole16(in);
224}
225
226template<>
227inline uint32_t convertToLittleEndian(uint32_t in) {
228    return htole32(in);
229}
230
231template<>
232inline int32_t convertToLittleEndian(int32_t in) {
233    return htole32(in);
234}
235
236template<>
237inline uint64_t convertToLittleEndian(uint64_t in) {
238    return htole64(in);
239}
240
241template<>
242inline int64_t convertToLittleEndian(int64_t in) {
243    return htole64(in);
244}
245
246} /*namespace img_utils*/
247} /*namespace android*/
248
249#endif /*IMG_UTILS_ENDIAN_UTILS*/
250
251