c2pa-cpp
C++ API for the C2PA SDK
Loading...
Searching...
No Matches
c2pa_internal.hpp
Go to the documentation of this file.
1// Copyright 2024 Adobe. All rights reserved.
2// This file is licensed to you under the Apache License,
3// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
4// or the MIT license (http://opensource.org/licenses/MIT),
5// at your option.
6// Unless required by applicable law or agreed to in writing,
7// this software is distributed on an "AS IS" BASIS, WITHOUT
8// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
9// implied. See the LICENSE-MIT and LICENSE-APACHE files for the
10// specific language governing permissions and limitations under
11// each license.
12
13/// @file c2pa_internal.hpp
14/// @brief Internal implementation details shared across c2pa_cpp source files.
15/// @details This header is private to the library implementation and not installed,
16/// as it is used to share code inside c2pa_cpp SDK.
17
18#ifndef C2PA_INTERNAL_HPP
19#define C2PA_INTERNAL_HPP
20
21#include <cstring>
22#include <fstream>
23#include <filesystem>
24#include <string>
25#include <vector>
26#include <memory>
27
28#include "c2pa.h"
29#include "c2pa.hpp"
30
31namespace c2pa {
32namespace detail {
33
34/// @brief True if the C2PA error message indicates no JUMBF / manifest in the asset (ManifestNotFound).
35inline bool error_indicates_manifest_not_found(const char* message) noexcept {
36 return message != nullptr && std::strstr(message, "ManifestNotFound") != nullptr;
37}
38
39/// @brief Converts a C array of C strings to a std::vector of std::string.
40/// @param mime_types Pointer to an array of C strings (const char*).
41/// @param count Number of elements in the array.
42/// @return A std::vector containing the strings from the input array.
43/// @details This function takes ownership of the input array and frees it
44/// using c2pa_free_string_array().
45inline std::vector<std::string> c_mime_types_to_vector(const char* const* mime_types, uintptr_t count) {
46 std::vector<std::string> result;
47 if (mime_types == nullptr) { return result; }
48
49 try {
50 result.reserve(count);
51 for(uintptr_t i = 0; i < count; i++) {
52 if (mime_types[i] != nullptr) {
53 result.emplace_back(mime_types[i]);
54 }
55 }
56 } catch (...) {
57 c2pa_free_string_array(mime_types, count);
58 throw;
59 }
60
61 c2pa_free_string_array(mime_types, count);
62 return result;
63}
64
65/// Maps C2PA seek mode to std::ios seek direction.
66constexpr std::ios_base::seekdir whence_to_seekdir(C2paSeekMode whence) noexcept {
67 switch (whence) {
68 case C2paSeekMode::Start: return std::ios_base::beg;
69 case C2paSeekMode::Current: return std::ios_base::cur;
70 case C2paSeekMode::End: return std::ios_base::end;
71 default: return std::ios_base::beg;
72 }
73}
74
75/// Check if stream is in valid state for I/O operations
76template<typename Stream>
77inline bool is_stream_usable(Stream* s) noexcept {
78 return s && !s->bad();
79}
80
81/// Traits (templated): how to seek and get position for a given stream type.
82template<typename Stream>
84
85template<>
86struct StreamSeekTraits<std::istream> {
87 static void seek(std::istream* s, intptr_t offset, std::ios_base::seekdir dir) {
88 s->seekg(offset, dir);
89 }
90 static int64_t tell(std::istream* s) {
91 return static_cast<int64_t>(s->tellg());
92 }
93};
94
95template<>
96struct StreamSeekTraits<std::ostream> {
97 static void seek(std::ostream* s, intptr_t offset, std::ios_base::seekdir dir) {
98 s->seekp(offset, dir);
99 }
100 static int64_t tell(std::ostream* s) {
101 return static_cast<int64_t>(s->tellp());
102 }
103};
104
105template<>
106struct StreamSeekTraits<std::iostream> {
107 static void seek(std::iostream* s, intptr_t offset, std::ios_base::seekdir dir) {
108 s->seekg(offset, dir);
109 s->seekp(offset, dir);
110 }
111 static int64_t tell(std::iostream* s) {
112 return static_cast<int64_t>(s->tellp());
113 }
114};
115
116/// Seeker impl.
117/// Exceptions must not unwind into Rust/C, so any throw
118/// is converted to an IoError return.
119template<typename Stream>
120intptr_t stream_seeker(StreamContext* context, intptr_t offset, C2paSeekMode whence) {
121 try {
122 auto* stream = reinterpret_cast<Stream*>(context);
123 if (!is_stream_usable(stream)) {
125 }
126 const std::ios_base::seekdir dir = whence_to_seekdir(whence);
127 stream->clear();
128 StreamSeekTraits<Stream>::seek(stream, offset, dir);
129 if (stream->fail()) {
131 }
132 if (stream->bad()) {
134 }
135 const int64_t pos = StreamSeekTraits<Stream>::tell(stream);
136 if (pos < 0) {
138 }
139 return static_cast<intptr_t>(pos);
140 } catch (...) {
142 }
143}
144
145/// Reader impl.
146/// Exceptions must not unwind into Rust/C, so any throw
147/// is converted to an IoError return.
148template<typename Stream>
149intptr_t stream_reader(StreamContext* context, uint8_t* buffer, intptr_t size) {
150 if (!context || !buffer) {
152 }
153 if (size < 0) {
155 }
156 if (size == 0) {
157 return 0;
158 }
159 try {
160 auto* stream = reinterpret_cast<Stream*>(context);
161 if (!is_stream_usable(stream)) {
163 }
164 stream->read(reinterpret_cast<char*>(buffer), size);
165 if (stream->fail()) {
166 if (!stream->eof()) {
168 }
169 }
170 if (stream->bad()) {
172 }
173 return static_cast<intptr_t>(stream->gcount());
174 } catch (...) {
176 }
177}
178
179/// Get stream from context, used by writer and flusher.
180/// Exceptions must not unwind into Rust/C, so any throw
181/// is converted to an IoError return.
182template<typename Stream, typename Op>
183intptr_t stream_op(StreamContext* context, Op op) {
184 try {
185 auto* stream = reinterpret_cast<Stream*>(context);
186 if (!is_stream_usable(stream)) {
188 }
189 const intptr_t result = op(stream);
190 if (stream->fail()) {
192 }
193 if (stream->bad()) {
195 }
196 return result;
197 } catch (...) {
199 }
200}
201
202/// Writer impl.
203template<typename Stream>
204intptr_t stream_writer(StreamContext* context, const uint8_t* buffer, intptr_t size) {
205 return stream_op<Stream>(context, [buffer, size](Stream* s) {
206 s->write(reinterpret_cast<const char*>(buffer), size);
207 return size;
208 });
209}
210
211/// Flusher impl.
212template<typename Stream>
214 return stream_op<Stream>(context, [](Stream* s) {
215 s->flush();
216 return 0;
217 });
218}
219
220/// @brief Open a binary file stream with error handling
221/// @tparam StreamType std::ifstream or std::ofstream
222/// @param path Path to the file
223/// @return Unique pointer to opened stream
224template<typename StreamType>
225inline std::unique_ptr<StreamType> open_file_binary(const std::filesystem::path &path)
226{
227 auto stream = std::make_unique<StreamType>(
228 path,
229 std::ios_base::binary
230 );
231 if (!stream->is_open()) {
232 throw C2paException("Failed to open file: " + path.string());
233 }
234 return stream;
235}
236
237/// @brief Extract file extension without the leading dot
238/// @param path Filesystem path
239/// @return Extension string (e.g., "jpg" not ".jpg")
240inline std::string extract_file_extension(const std::filesystem::path &path) noexcept {
241 auto ext = path.extension().string();
242 return ext.empty() ? "" : ext.substr(1);
243}
244
245/// @brief Convert C string result to C++ string with cleanup
246/// @param c_result Raw C string from C API
247/// @return C++ string (throws if null)
248template<typename T>
249inline std::string c_string_to_string(T* c_result) {
250 if (c_result == nullptr) {
251 throw C2paException();
252 }
253 std::string str(c_result);
255 return str;
256}
257
258/// @brief Convert C byte array result to C++ vector
259/// @param data Raw byte array from C API
260/// @param size Size of the byte array (result from C API call)
261/// @return Vector containing the bytes (throws if null or negative size)
262/// @details This helper extracts the pattern of checking C API results,
263/// copying to a vector, and freeing the C-allocated memory.
264/// The C API contract is: if result < 0, the operation failed. A null
265/// data pointer with size == 0 is a valid empty result (the C API
266/// returns null for empty byte arrays).
267inline std::vector<unsigned char> to_byte_vector(const unsigned char* data, int64_t size) {
268 if (size < 0 || (data == nullptr && size > 0)) {
269 c2pa_free(data); // May be null or allocated, c2pa_free handles both
270 throw C2paException();
271 }
272 if (size == 0) {
274 return {};
275 }
276
277 auto result = std::vector<unsigned char>(data, data + size);
279 return result;
280}
281
282} // namespace detail
283} // namespace c2pa
284
285#endif // C2PA_INTERNAL_HPP
C++ wrapper for the C2PA C library.
Exception class for C2pa errors. This class is used to throw exceptions for errors encountered by the...
Definition c2pa.hpp:87
std::vector< std::string > c_mime_types_to_vector(const char *const *mime_types, uintptr_t count)
Converts a C array of C strings to a std::vector of std::string.
Definition c2pa_internal.hpp:45
intptr_t stream_writer(StreamContext *context, const uint8_t *buffer, intptr_t size)
Writer impl.
Definition c2pa_internal.hpp:204
intptr_t stream_op(StreamContext *context, Op op)
Definition c2pa_internal.hpp:183
std::string extract_file_extension(const std::filesystem::path &path) noexcept
Extract file extension without the leading dot.
Definition c2pa_internal.hpp:240
bool error_indicates_manifest_not_found(const char *message) noexcept
True if the C2PA error message indicates no JUMBF / manifest in the asset (ManifestNotFound).
Definition c2pa_internal.hpp:35
intptr_t stream_flusher(StreamContext *context)
Flusher impl.
Definition c2pa_internal.hpp:213
constexpr std::ios_base::seekdir whence_to_seekdir(C2paSeekMode whence) noexcept
Maps C2PA seek mode to std::ios seek direction.
Definition c2pa_internal.hpp:66
bool is_stream_usable(Stream *s) noexcept
Check if stream is in valid state for I/O operations.
Definition c2pa_internal.hpp:77
std::vector< unsigned char > to_byte_vector(const unsigned char *data, int64_t size)
Convert C byte array result to C++ vector.
Definition c2pa_internal.hpp:267
intptr_t stream_reader(StreamContext *context, uint8_t *buffer, intptr_t size)
Definition c2pa_internal.hpp:149
std::string c_string_to_string(T *c_result)
Convert C string result to C++ string with cleanup.
Definition c2pa_internal.hpp:249
intptr_t stream_seeker(StreamContext *context, intptr_t offset, C2paSeekMode whence)
Definition c2pa_internal.hpp:120
std::unique_ptr< StreamType > open_file_binary(const std::filesystem::path &path)
Open a binary file stream with error handling.
Definition c2pa_internal.hpp:225
Definition c2pa.hpp:52
int stream_error_return(StreamError e) noexcept
Set errno from StreamError and return error sentinel.
Definition c2pa.hpp:79
static void seek(std::iostream *s, intptr_t offset, std::ios_base::seekdir dir)
Definition c2pa_internal.hpp:107
static int64_t tell(std::iostream *s)
Definition c2pa_internal.hpp:111
static int64_t tell(std::istream *s)
Definition c2pa_internal.hpp:90
static void seek(std::istream *s, intptr_t offset, std::ios_base::seekdir dir)
Definition c2pa_internal.hpp:87
static void seek(std::ostream *s, intptr_t offset, std::ios_base::seekdir dir)
Definition c2pa_internal.hpp:97
static int64_t tell(std::ostream *s)
Definition c2pa_internal.hpp:100
Traits (templated): how to seek and get position for a given stream type.
Definition c2pa_internal.hpp:83