fb-cpp 0.0.1
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
Blob.cpp
1/*
2 * MIT License
3 *
4 * Copyright (c) 2025 Adriano dos Santos Fernandes
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to do so, subject to the
11 * following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#include "Blob.h"
26#include "Attachment.h"
27#include "Client.h"
28#include "Transaction.h"
29#include "fb-api/firebird/impl/inf_pub.h"
30#include <cstdint>
31#include <cstring>
32#include <limits>
33#include <vector>
34
35using namespace fbcpp;
36using namespace fbcpp::impl;
37
38Blob::Blob(Attachment& attachment, Transaction& transaction, const BlobOptions& options)
39 : attachment{attachment},
40 transaction{transaction},
41 status{attachment.getClient().newStatus()},
42 statusWrapper{attachment.getClient(), status.get()}
43{
44 assert(attachment.isValid());
45 assert(transaction.isValid());
46
47 const auto preparedBpb = prepareBpb(options);
48
49 handle.reset(attachment.getHandle()->createBlob(&statusWrapper, transaction.getHandle().get(), &id.id,
50 static_cast<unsigned>(preparedBpb.size()), preparedBpb.data()));
51}
52
53Blob::Blob(Attachment& attachment, Transaction& transaction, const BlobId& blobId, const BlobOptions& options)
54 : attachment{attachment},
55 transaction{transaction},
56 id{blobId},
57 status{attachment.getClient().newStatus()},
58 statusWrapper{attachment.getClient(), status.get()}
59{
60 assert(attachment.isValid());
61 assert(transaction.isValid());
62
63 const auto preparedBpb = prepareBpb(options);
64
65 handle.reset(attachment.getHandle()->openBlob(&statusWrapper, transaction.getHandle().get(), &id.id,
66 static_cast<unsigned>(preparedBpb.size()), preparedBpb.data()));
67}
68
69std::vector<std::uint8_t> Blob::prepareBpb(const BlobOptions& options)
70{
71 const auto util = attachment.getClient().getUtil();
72
73 auto builder = fbUnique(util->getXpbBuilder(&statusWrapper, fb::IXpbBuilder::BPB,
74 reinterpret_cast<const std::uint8_t*>(options.getBpb().data()),
75 static_cast<unsigned>(options.getBpb().size())));
76
77 if (const auto type = options.getType(); type.has_value())
78 builder->insertInt(&statusWrapper, isc_bpb_type, static_cast<int>(type.value()));
79
80 if (const auto sourceType = options.getSourceType(); sourceType.has_value())
81 builder->insertInt(&statusWrapper, isc_bpb_source_type, static_cast<int>(sourceType.value()));
82
83 if (const auto targetType = options.getTargetType(); targetType.has_value())
84 builder->insertInt(&statusWrapper, isc_bpb_target_type, static_cast<int>(targetType.value()));
85
86 if (const auto sourceCharSet = options.getSourceCharSet(); sourceCharSet.has_value())
87 builder->insertInt(&statusWrapper, isc_bpb_source_interp, static_cast<int>(sourceCharSet.value()));
88
89 if (const auto targetCharSet = options.getTargetCharSet(); targetCharSet.has_value())
90 builder->insertInt(&statusWrapper, isc_bpb_target_interp, static_cast<int>(targetCharSet.value()));
91
92 if (const auto storage = options.getStorage(); storage.has_value())
93 builder->insertInt(&statusWrapper, isc_bpb_storage, static_cast<int>(storage.value()));
94
95 const auto buffer = builder->getBuffer(&statusWrapper);
96 const auto length = builder->getBufferLength(&statusWrapper);
97
98 std::vector<std::uint8_t> bpb(length);
99
100 if (length != 0)
101 std::memcpy(bpb.data(), buffer, length);
102
103 return bpb;
104}
105
107{
108 assert(isValid());
109
110 const std::uint8_t items[] = {isc_info_blob_total_length};
111 std::uint8_t buffer[16]{};
112
113 handle->getInfo(&statusWrapper, sizeof(items), items, sizeof(buffer), buffer);
114
115 const auto* ptr = buffer;
116 const auto* end = buffer + sizeof(buffer);
117
118 while (ptr < end)
119 {
120 const auto item = *ptr++;
121
122 if (item == isc_info_end)
123 break;
124
125 if (item == isc_info_truncated)
126 throw FbCppException("Blob::getLength truncated response");
127
128 if (item == isc_info_error)
129 throw FbCppException("Blob::getLength error response");
130
131 if (ptr + 2 > end)
132 throw FbCppException("Blob::getLength malformed response");
133
134 const auto itemLength = static_cast<std::uint16_t>((ptr[0]) | (ptr[1] << 8));
135 ptr += 2;
136
137 if (ptr + itemLength > end)
138 throw FbCppException("Blob::getLength invalid length");
139
140 if (item == isc_info_blob_total_length)
141 {
142 unsigned result = 0;
143
144 for (std::uint16_t i = 0; i < itemLength; ++i)
145 result |= static_cast<unsigned>(ptr[i]) << (8u * i);
146
147 return result;
148 }
149
150 ptr += itemLength;
151 }
152
153 throw FbCppException("Blob::getLength value not found");
154}
155
156unsigned Blob::read(std::span<std::byte> buffer)
157{
158 assert(isValid());
159
160 unsigned totalRead = 0;
161 const unsigned maxChunkSize = std::numeric_limits<std::uint16_t>::max();
162
163 while (!buffer.empty())
164 {
165 const auto chunkSize = buffer.size() < maxChunkSize ? buffer.size() : maxChunkSize;
166 const auto chunk = buffer.first(chunkSize);
167 const auto readNow = readSegment(chunk);
168
169 if (readNow == 0)
170 break;
171
172 totalRead += readNow;
173 buffer = buffer.subspan(readNow);
174 }
175
176 return totalRead;
177}
178
179unsigned Blob::readSegment(std::span<std::byte> buffer)
180{
181 assert(isValid());
182
183 if (buffer.empty())
184 return 0;
185
186 unsigned segmentLength = 0;
187 const auto result =
188 handle->getSegment(&statusWrapper, static_cast<unsigned>(buffer.size()), buffer.data(), &segmentLength);
189
190 switch (result)
191 {
192 case fb::IStatus::RESULT_OK:
193 case fb::IStatus::RESULT_SEGMENT:
194 return segmentLength;
195
196 case fb::IStatus::RESULT_NO_DATA:
197 return 0;
198
199 default:
200 return 0;
201 }
202}
203
204void Blob::write(std::span<const std::byte> buffer)
205{
206 assert(isValid());
207
208 const unsigned maxChunkSize = std::numeric_limits<std::uint16_t>::max();
209
210 while (!buffer.empty())
211 {
212 const auto chunkSize = buffer.size() < maxChunkSize ? buffer.size() : maxChunkSize;
213 const auto chunk = buffer.first(chunkSize);
214 writeSegment(chunk);
215 buffer = buffer.subspan(chunkSize);
216 }
217}
218
219void Blob::writeSegment(std::span<const std::byte> buffer)
220{
221 assert(isValid());
222
223 if (buffer.empty())
224 return;
225
226 if (buffer.size() > std::numeric_limits<unsigned>::max())
227 throw FbCppException("Segment too large");
228
229 handle->putSegment(&statusWrapper, static_cast<unsigned>(buffer.size()), buffer.data());
230}
231
232int Blob::seek(BlobSeekMode mode, int offset)
233{
234 assert(isValid());
235 return handle->seek(&statusWrapper, static_cast<int>(mode), offset);
236}
237
239{
240 assert(isValid());
241
242 handle->cancel(&statusWrapper);
243 handle.reset();
244}
245
247{
248 assert(isValid());
249
250 handle->close(&statusWrapper);
251 handle.reset();
252}
Represents a connection to a Firebird database.
Definition Attachment.h:177
Client & getClient() noexcept
Returns the Client object reference used to create this Attachment object.
Definition Attachment.h:230
FbRef< fb::IAttachment > getHandle() noexcept
Returns the internal Firebird IAttachment handle.
Definition Attachment.h:238
bool isValid() noexcept
Returns whether the Attachment object is valid.
Definition Attachment.h:222
Represents a Firebird blob identifier.
Definition Blob.h:52
Additional options used when creating or opening blobs.
Definition Blob.h:89
const std::optional< BlobType > getSourceType() const
Retrieves the source blob subtype.
Definition Blob.h:137
const std::optional< BlobType > getType() const
Retrieves the blob type to be used for blob operations.
Definition Blob.h:120
const std::optional< BlobType > getTargetType() const
Retrieves the target blob subtype.
Definition Blob.h:154
const std::optional< BlobStorage > getStorage() const
Retrieves the blob storage mode.
Definition Blob.h:205
const std::vector< std::uint8_t > & getBpb() const noexcept
Retrieves the blob parameter block (BPB) used during blob operations.
Definition Blob.h:94
const std::optional< std::int16_t > getTargetCharSet() const
Retrieves the target character set identifier.
Definition Blob.h:188
const std::optional< std::int16_t > getSourceCharSet() const
Retrieves the source character set identifier.
Definition Blob.h:171
bool isValid() const noexcept
Returns whether the blob handle is valid.
Definition Blob.h:305
unsigned read(std::span< std::byte > buffer)
Reads data from the blob into the provided buffer.
Definition Blob.cpp:156
unsigned getLength()
Retrieves the length of the blob in bytes.
Definition Blob.cpp:106
void cancel()
Cancels any changes performed on the blob and releases the handle.
Definition Blob.cpp:238
void write(std::span< const std::byte > buffer)
Writes data from the buffer into the blob.
Definition Blob.cpp:204
int seek(BlobSeekMode mode, int offset)
Repositions the blob read/write cursor.
Definition Blob.cpp:232
unsigned readSegment(std::span< std::byte > buffer)
Reads a single segment from the blob into the provided buffer.
Definition Blob.cpp:179
Blob(Attachment &attachment, Transaction &transaction, const BlobOptions &options={})
Creates and opens a new blob for writing.
Definition Blob.cpp:38
void writeSegment(std::span< const std::byte > buffer)
Writes a single segment from the buffer into the blob.
Definition Blob.cpp:219
void close()
Closes the blob and finalizes any pending changes.
Definition Blob.cpp:246
fb::IUtil * getUtil()
Returns a Firebird IUtil interface.
Definition Client.h:142
Represents a transaction in a Firebird database.
bool isValid() noexcept
Returns whether the Transaction object is valid.
FbRef< fb::ITransaction > getHandle() noexcept
Returns the internal Firebird ITransaction handle.
fb-cpp namespace.
Definition Attachment.h:42
BlobSeekMode
Defines the origin used when repositioning a blob.
Definition Blob.h:233