BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlde_hexencoder.h
Go to the documentation of this file.
1/// @file bdlde_hexencoder.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlde_hexencoder.h -*-C++-*-
8#ifndef INCLUDED_BDLDE_HEXENCODER
9#define INCLUDED_BDLDE_HEXENCODER
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bdlde_hexencoder bdlde_hexencoder
15/// @brief Provide mechanism for encoding text into hexadecimal.
16/// @addtogroup bdl
17/// @{
18/// @addtogroup bdlde
19/// @{
20/// @addtogroup bdlde_hexencoder
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bdlde_hexencoder-purpose"> Purpose</a>
25/// * <a href="#bdlde_hexencoder-classes"> Classes </a>
26/// * <a href="#bdlde_hexencoder-description"> Description </a>
27/// * <a href="#bdlde_hexencoder-hex-encoding"> Hex Encoding </a>
28/// * <a href="#bdlde_hexencoder-hex-decoding"> Hex Decoding </a>
29/// * <a href="#bdlde_hexencoder-usage"> Usage </a>
30/// * <a href="#bdlde_hexencoder-example-1-basic-usage-of-bdlde-hexencoder"> Example 1: Basic Usage of bdlde::HexEncoder </a>
31///
32/// # Purpose {#bdlde_hexencoder-purpose}
33/// Provide mechanism for encoding text into hexadecimal.
34///
35/// # Classes {#bdlde_hexencoder-classes}
36///
37/// - bdlde::HexEncoder: mechanism for encoding text into hexadecimal
38///
39/// @see bdlde_hexdecoder
40///
41/// # Description {#bdlde_hexencoder-description}
42/// This component provides a class, `bdlde::HexEncoder`, for
43/// encoding plain text into its hexadecimal representation.
44///
45/// `bdlde::HexEncoder` and `bdlde::HexDecoder` provide a pair of template
46/// functions (each parameterized separately on both input and output iterators)
47/// that can be used respectively to encode and to decode byte sequences of
48/// arbitrary length into and from the printable Hex representation.
49///
50/// Each instance of either the encoder or decoder retains the state of the
51/// conversion from one supplied input to the next, enabling the processing of
52/// segmented input -- i.e., processing resumes where it left off with the next
53/// invocation on new input. Instance methods are provided for both the
54/// encoder and decoder to (1) assert the end of input, (2) determine whether
55/// the input so far is currently acceptable, and (3) indicate whether a
56/// non-recoverable error has occurred.
57///
58/// ## Hex Encoding {#bdlde_hexencoder-hex-encoding}
59///
60///
61/// The data stream is processed one byte at a time from left to right. Each
62/// byte
63/// @code
64/// 7 6 5 4 3 2 1 0
65/// +-+-+-+-+-+-+-+-+
66/// | |
67/// +-+-+-+-+-+-+-+-+
68/// `------v------'
69/// Byte
70/// @endcode
71/// is segmented into two intermediate 4-bit quantities.
72/// @code
73/// 3 2 1 0 3 2 1 0
74/// +-+-+-+-+-+-+-+-+
75/// | | |
76/// +-+-+-+-+-+-+-+-+
77/// `--v--' `--v--'
78/// char0 char1
79/// @endcode
80/// Each 4-bit quantity is in turn used as an index into the following character
81/// table to generate an 8-bit character.
82/// @code
83/// =================
84/// * Hex Alphabet *
85/// -----------------
86/// Val Enc Val Enc
87/// --- --- --- ---
88/// 0 '0' 8 '8'
89/// 1 '1' 9 '9'
90/// 2 '2' 10 'A'
91/// 3 '3' 11 'B'
92/// 4 '4' 12 'C'
93/// 5 '5' 13 'D'
94/// 6 '6' 14 'E'
95/// 7 '7' 15 'F'
96/// =================
97/// @endcode
98/// Depending on the settings encoder represents values from 10 to 15 as
99/// uppercase (`A`-`F`) or lowercase letters(`a`-`f`).
100///
101/// Input values of increasing length along with their corresponding Hex
102/// encodings are illustrated below:
103/// @code
104/// Data: /* nothing */
105/// Encoding: /* nothing */
106///
107/// Data: "0" (0011 0000)
108/// Encoding: 30
109///
110/// Data: "01" (0011 0000 0011 0001)
111/// Encoding: 3031
112///
113/// Data: "01A" (0011 0000 0011 0001 1000 0001)
114/// Encoding: 303141
115///
116/// Data: "01A?" (0011 0000 0011 0001 1000 0001 0011 1111)
117/// Encoding: 3031413F
118/// @endcode
119///
120/// ## Hex Decoding {#bdlde_hexencoder-hex-decoding}
121///
122///
123/// The data stream is processed two bytes at a time from left to right. Each
124/// sequence of two 8-bit quantities
125/// @code
126/// 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
127/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
128/// | | |
129/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
130/// `------v------' `------v------'
131/// Byte0 Byte1
132/// @endcode
133/// is segmented into four intermediate 4-bit quantities.
134/// @code
135/// 3 2 1 0 3 2 1 0 3 2 1 0 3 2 1 0
136/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
137/// | | | | |
138/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
139/// `--v--' `--v--' `--v--' `--v--'
140/// chunk0 chunk1 chunk2 chunk3
141/// @endcode
142/// The second and forth chunks are combined to get the resulting 8-bit
143/// character.
144///
145/// Whitespace characters are ignored. On any non-alphabet character the
146/// decoder reports an error. In order for a Hex encoding to be valid the
147/// length of the input data (excluding any whitespace characters) must be a
148/// multiple of two.
149///
150/// Input values of increasing length along with their corresponding Hex
151/// encodings are illustrated below (note that the encoded whitespace character
152/// is skipped and the resulting string does not contain it):
153/// @code
154/// Data: /* nothing */
155/// Encoding: /* nothing */
156///
157/// Data: "4" (0000 0100)
158/// Encoding: /* nothing */
159///
160/// Data: "41" (0000 0100 0000 0001)
161/// Encoding: A
162///
163/// Data: "412" (0000 0100 0000 0001 0000 0010)
164/// Encoding: A
165///
166/// Data: "4120" (0000 0100 0000 0001 0000 0010 0000 0000)
167/// Encoding: A
168///
169/// Data: "41203" (0000 0100 0000 0001 0000 0010 0000 0000
170/// 0000 0011)
171/// Encoding: A
172///
173/// Data: "41203F" (0011 0000 0011 0001 1000 0001 0010 0011
174/// 0000 0011 0000 1111)
175/// Encoding: A?
176/// @endcode
177///
178/// ## Usage {#bdlde_hexencoder-usage}
179///
180///
181/// This section illustrates intended use of this component.
182///
183/// ### Example 1: Basic Usage of bdlde::HexEncoder {#bdlde_hexencoder-example-1-basic-usage-of-bdlde-hexencoder}
184///
185///
186/// The following example shows using a `bdlde::HexEncoder` object to encode
187/// bytes into a hexidecimal format. For dependency reasons, a more complete
188/// example, showing both encoding and decoding can be found in
189/// @ref bdlde_hexdecoder .
190///
191/// In the example below, we implement a function `streamEncoder`, that reads
192/// text from `bsl::istream`, encodes that text into hex representation, and
193/// writes the encoded text to a `bsl::ostream`. `streamEncoder` returns 0 on
194/// success and a negative value if the input data could not be successfully
195/// encoded or if there is an I/O error.
196/// @code
197/// /// Read the entire contents of the specified input stream `is`, convert
198/// /// the input plain text to hex representation, and write the encoded
199/// /// text to the specified output stream `os`. Return 0 on success, and
200/// /// a negative value otherwise.
201/// int streamEncoder(bsl::ostream& os, bsl::istream& is)
202/// {
203/// enum {
204/// SUCCESS = 0,
205/// ENCODE_ERROR = -1,
206/// IO_ERROR = -2
207/// };
208/// @endcode
209/// First we create an object, create buffers for storing data, and start loop
210/// that runs while the input stream contains some data:
211/// @code
212/// bdlde::HexEncoder converter;
213///
214/// const int INBUFFER_SIZE = 1 << 10;
215/// const int OUTBUFFER_SIZE = 1 << 10;
216///
217/// char inputBuffer[INBUFFER_SIZE];
218/// char outputBuffer[OUTBUFFER_SIZE];
219///
220/// char *output = outputBuffer;
221/// char *outputEnd = outputBuffer + sizeof outputBuffer;
222///
223/// while (is.good()) { // input stream not exhausted
224/// @endcode
225/// On each iteration we read some data from the input stream:
226/// @code
227/// is.read(inputBuffer, sizeof inputBuffer);
228///
229/// const char *input = inputBuffer;
230/// const char *inputEnd = input + is.gcount();
231///
232/// while (input < inputEnd) { // input encoding not complete
233///
234/// int numOut;
235/// int numIn;
236/// @endcode
237/// Convert obtained text using `bdlde::HexEncoder`:
238/// @code
239/// int status = converter.convert(
240/// output,
241/// &numOut,
242/// &numIn,
243/// input,
244/// inputEnd,
245/// static_cast<int>(outputEnd - output));
246/// if (status < 0) {
247/// return ENCODE_ERROR; // RETURN
248/// }
249///
250/// output += numOut;
251/// input += numIn;
252/// @endcode
253/// And write encoded text to the output stream:
254/// @code
255/// if (output == outputEnd) { // output buffer full; write data
256/// os.write(outputBuffer, sizeof outputBuffer);
257/// if (os.fail()) {
258/// return IO_ERROR; // RETURN
259/// }
260/// output = outputBuffer;
261/// }
262/// }
263/// }
264///
265/// while (1) {
266/// int numOut = 0;
267/// @endcode
268/// Then, we need to store the unhandled symbol (if there is one) to the output
269/// buffer and complete the work of our encoder:
270/// @code
271/// int more = converter.endConvert(
272/// output,
273/// &numOut,
274/// static_cast<int>(outputEnd - output));
275/// if (more < 0) {
276/// return ENCODE_ERROR; // RETURN
277/// }
278///
279/// output += numOut;
280///
281/// if (!more) { // no more output
282/// break;
283/// }
284///
285/// assert(output == outputEnd); // output buffer is full
286///
287/// os.write(outputBuffer, sizeof outputBuffer); // write buffer
288/// if (os.fail()) {
289/// return IO_ERROR; // RETURN
290/// }
291/// output = outputBuffer;
292/// }
293///
294/// if (output > outputBuffer) {
295/// os.write(outputBuffer, output - outputBuffer);
296/// }
297///
298/// return is.eof() && os.good() ? SUCCESS : IO_ERROR;
299/// }
300/// @endcode
301/// Next, to demonstrate how our function works we need to create a stream with
302/// data to encode. Assume that we have some character buffer,
303/// `BLOOMBERG_NEWS`, and a function, `streamDecoder` mirroring the work of the
304/// `streamEncoder`. Below we should encode this string into a hexidecimal
305/// format:
306/// @code
307/// bsl::istringstream inStream(bsl::string(BLOOMBERG_NEWS,
308/// strlen(BLOOMBERG_NEWS)));
309/// bsl::stringstream outStream;
310/// bsl::stringstream backInStream;
311/// @endcode
312/// Then, we use our function to encode text:
313/// @code
314/// assert(0 == streamEncoder(outStream, inStream));
315/// @endcode
316/// This example does *not* decode the resulting hexidecimal text, for a
317/// more complete example, see @ref bdlde_hexdecoder .
318/// @}
319/** @} */
320/** @} */
321
322/** @addtogroup bdl
323 * @{
324 */
325/** @addtogroup bdlde
326 * @{
327 */
328/** @addtogroup bdlde_hexencoder
329 * @{
330 */
331
332#include <bdlscm_version.h>
333
334#include <bsls_assert.h>
335
336
337namespace bdlde {
338
339 // ================
340 // class HexEncoder
341 // ================
342
343/// This class implements a mechanism capable of converting data of
344/// arbitrary length to its corresponding Hex representation.
345///
346/// See @ref bdlde_hexencoder
348
349 // PRIVATE TYPES
350
351 /// Symbolic state values for the encoder
352 enum States {
353 e_ERROR_STATE = -1, // input is irreparably invalid
354 e_INPUT_STATE = 0, // general input state
355 e_DONE_STATE = 1 // any additional input is error
356 };
357
358 // DATA
359 int d_state; // current state of this object
360
361 char d_deferred; // retained output character
362
363 int d_outputLength; // total number of output characters
364
365 bool d_upperCaseFlag; // flag to indicate if uppercase letters are
366 // used
367
368 const char *d_encodeTable_p; // hexadecimal alphabet
369
370
371 private:
372 // NOT IMPLEMENTED
373 HexEncoder(const HexEncoder&);
374 HexEncoder& operator=(const HexEncoder&);
375
376 public:
377 // CREATORS
378
379 /// Create a Hex encoder in the initial state. Optionally specify the
380 /// `upperCaseLetters` to indicate if values from 10 to 15 are encoded
381 /// as uppercase letters(`A`-`F`) or as lowercase letters(`a`-`f`).
382 explicit HexEncoder(bool upperCaseLetters = true);
383
384 /// Destroy this object.
385 ~HexEncoder() = default;
386
387 // MANIPULATORS
388
389 /// Append to the buffer addressed by the specified `out` pending
390 /// character (if there is such) up to the optionally specified
391 /// `maxNumOut` limit (default is negative, meaning no limit). When
392 /// there is no pending output and `maxNumOut` is still not reached,
393 /// begin to consume and encode a sequence of input characters starting
394 /// at the specified `begin` position, up to but not including the
395 /// specified `end` position. Any resulting output is written to the
396 /// `out` buffer up to the (cumulative) `maxNumOut` limit. If
397 /// `maxNumOut` limit is reached, no further input will be consumed.
398 /// Load into the (optionally) specified `numOut` and `numIn` the number
399 /// of output bytes produced and input bytes consumed, respectively.
400 /// Return a non-negative value on success and a negative value
401 /// otherwise. A successful return status indicates the number of
402 /// characters that would be output if `endConvert` were called
403 /// subsequently with no output limit. These bytes *may* be available
404 /// for output if this method is called with a sufficiently large `maxNumOut`.
405 ///
406 /// \note Note that calling this method after `endConvert` has
407 /// been invoked without an intervening `reset` call will place this
408 /// instance in an error state, and return an error status. Note also
409 /// that it is recommended that after all calls to `convert` are
410 /// finished, the `endConvert` method be called to complete the encoding
411 /// of any unprocessed input characters.
412 template <class OUTPUT_ITERATOR, class INPUT_ITERATOR>
413 int convert(OUTPUT_ITERATOR out,
414 INPUT_ITERATOR begin,
415 INPUT_ITERATOR end);
416 template <class OUTPUT_ITERATOR, class INPUT_ITERATOR>
417 int convert(OUTPUT_ITERATOR out,
418 int *numOut,
419 int *numIn,
420 INPUT_ITERATOR begin,
421 INPUT_ITERATOR end,
422 int maxNumOut = -1);
423
424 /// Terminate encoding for this encoder; write any retained output
425 /// (e.g., from a previous call to `convert` with a non-zero output
426 /// limit argument) to the specified `out` buffer. Optionally specify
427 /// the `maxNumOut` limit on the number of bytes to output; if
428 /// `maxNumOut` is negative, no limit is imposed. Load into the
429 /// (optionally) specified `numOut` the number of output bytes produced.
430 /// Return a non-negative value on success and a negative value
431 /// otherwise. A successful return status indicates the number of
432 /// characters that would be output if `endConvert` were called
433 /// subsequently with no output limit. Any retained bytes are available
434 /// on a subsequent call to `endConvert`. Once this method is called,
435 /// no additional input may be supplied without an intervening call to
436 /// `reset`; once this method returns a zero status, a subsequent call
437 /// will place this encoder in the error state, and return an error
438 /// status.
439 template <class OUTPUT_ITERATOR>
440 int endConvert(OUTPUT_ITERATOR out);
441 template <class OUTPUT_ITERATOR>
442 int endConvert(OUTPUT_ITERATOR out, int *numOut, int maxNumOut = -1);
443
444 /// Reset this encoder to its initial state (i.e., as if no input had
445 /// been consumed).
446 void reset();
447
448 // ACCESSORS
449
450 /// Return `true` if the input read so far by this encoder is considered
451 /// syntactically complete, and `false` otherwise.
452 bool isAcceptable() const;
453
454 /// Return `true` if this encoder is in the done state (i.e.,
455 /// `endConvert` has been called and any additional input will result in
456 /// an error), and if there is no pending output, and `false` otherwise.
457 bool isDone() const;
458
459 /// Return `true` if there is no possibility of achieving an "acceptable" result, and `false` otherwise.
460 ///
461 /// \note Note that for an
462 /// encoder, no input can cause an error; the possible errors result
463 /// either from a call to the `convert` method after the `endConvert`
464 /// method is called the first time, or from a call to the `endConvert`
465 /// method after the `endConvert` method has returned successfully.
466 bool isError() const;
467
468 /// Return `true` if this encoder is in the initial state (i.e., as if
469 /// no input had been consumed), and `false` otherwise.
470 bool isInitialState() const;
471
472 /// Return `true` if this encoder represents values from 10 to 15 as
473 /// uppercase letters(`A`-`F`), and `false` if these values are
474 /// represented as lowercase letters(`a`-`f`).
475 bool isUpperCase() const;
476
477 /// Return the number of characters that would be output if `endConvert`
478 /// were called with no output limit.
479 int numOutputPending() const;
480
481 /// Return the total length of the output emitted by this encoder
482 /// (possibly after one or more calls to the `convert` or the `input`
483 /// methods) since its initial construction or the latest `reset`.
484 int outputLength() const;
485};
486
487// ============================================================================
488// INLINE DEFINITIONS
489// ============================================================================
490
491// MANIPULATORS
492template <class OUTPUT_ITERATOR, class INPUT_ITERATOR>
493int HexEncoder::convert(OUTPUT_ITERATOR out,
494 INPUT_ITERATOR begin,
495 INPUT_ITERATOR end)
496{
497 int dummyNumOut;
498 int dummyNumIn;
499
500 return convert(out, &dummyNumOut, &dummyNumIn, begin, end, -1);
501}
502
503template <class OUTPUT_ITERATOR, class INPUT_ITERATOR>
504int HexEncoder::convert(OUTPUT_ITERATOR out,
505 int *numOut,
506 int *numIn,
507 INPUT_ITERATOR begin,
508 INPUT_ITERATOR end,
509 int maxNumOut)
510{
511 BSLS_ASSERT(numOut);
512 BSLS_ASSERT(numIn);
513
514 if (e_ERROR_STATE == d_state || e_DONE_STATE == d_state) {
515 int rv = e_DONE_STATE == d_state ? -2 : -1;
516 d_state = e_ERROR_STATE;
517 *numOut = 0;
518 *numIn = 0;
519 return rv; // RETURN
520 }
521
522 if (0 == maxNumOut) {
523 *numOut = 0;
524 *numIn = 0;
525 return 0; // RETURN
526 }
527
528 int numConsumed = 0;
529 int numEmitted = 0;
530
531 // First we need to output pending symbol left over from the previous call.
532
533 if (d_deferred) {
534 *out = d_deferred;
535 ++out;
536 ++numEmitted;
537 d_deferred = 0;
538 }
539
540 // Then we can handle new input.
541
542 while (begin != end && numEmitted != maxNumOut) {
543 if (d_deferred) {
544 *out = d_deferred;
545 ++out;
546 ++numEmitted;
547 d_deferred = 0;
548 ++begin;
549 }
550 else {
551 const char digit = static_cast<char>(*begin);
552 ++numConsumed;
553
554 *out = d_encodeTable_p[(digit >> 4) & 0x0f];
555 ++out;
556 ++numEmitted;
557 d_deferred = d_encodeTable_p[digit & 0x0f];
558 }
559 }
560
561 *numOut = numEmitted;
562 d_outputLength += numEmitted;
563 *numIn = numConsumed;
564 return d_deferred ? 1 : 0;
565}
566
567template <class OUTPUT_ITERATOR>
568int HexEncoder::endConvert(OUTPUT_ITERATOR out)
569{
570 int dummyNumOut;
571
572 return endConvert(out, &dummyNumOut, -1);
573}
574
575template <class OUTPUT_ITERATOR>
576int HexEncoder::endConvert(OUTPUT_ITERATOR out, int *numOut, int maxNumOut)
577{
578 BSLS_ASSERT(numOut);
579
580 if (e_ERROR_STATE == d_state) {
581 return -1; // RETURN
582 }
583
584 if (e_DONE_STATE == d_state && !d_deferred) {
585 d_state = e_ERROR_STATE;
586 return -1; // RETURN
587 }
588
589 d_state = e_DONE_STATE;
590
591 if (d_deferred) {
592 if (0 == maxNumOut) {
593 return 1; // RETURN
594 }
595 else {
596 *out = d_deferred;
597 *numOut = 1;
598 d_deferred = 0;
599 d_outputLength++;
600 }
601 }
602
603 return 0;
604}
605
606inline
608{
609 d_state = e_INPUT_STATE;
610 d_deferred = 0;
611 d_outputLength = 0;
612}
613
614// ACCESSORS
615inline
617{
618 return e_INPUT_STATE == d_state && 0 == d_deferred;
619}
620
621inline
623{
624 return e_DONE_STATE == d_state && 0 == d_deferred;
625}
626
627inline
629{
630 return e_ERROR_STATE == d_state;
631}
632
633inline
635{
636 return e_INPUT_STATE == d_state && 0 == d_outputLength;
637}
638
639inline
641{
642 return d_upperCaseFlag;
643}
644
645inline
647{
648 return d_deferred ? 1 : 0;
649}
650
651inline
653{
654 return d_outputLength;
655}
656
657} // close package namespace
658
659
660#endif
661
662// ----------------------------------------------------------------------------
663// Copyright 2022 Bloomberg Finance L.P.
664//
665// Licensed under the Apache License, Version 2.0 (the "License");
666// you may not use this file except in compliance with the License.
667// You may obtain a copy of the License at
668//
669// http://www.apache.org/licenses/LICENSE-2.0
670//
671// Unless required by applicable law or agreed to in writing, software
672// distributed under the License is distributed on an "AS IS" BASIS,
673// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
674// See the License for the specific language governing permissions and
675// limitations under the License.
676// ----------------------------- END-OF-FILE ----------------------------------
677
678/** @} */
679/** @} */
680/** @} */
Definition bdlde_hexencoder.h:347
bool isAcceptable() const
Definition bdlde_hexencoder.h:616
int outputLength() const
Definition bdlde_hexencoder.h:652
bool isUpperCase() const
Definition bdlde_hexencoder.h:640
bool isError() const
Definition bdlde_hexencoder.h:628
int convert(OUTPUT_ITERATOR out, INPUT_ITERATOR begin, INPUT_ITERATOR end)
Definition bdlde_hexencoder.h:493
bool isDone() const
Definition bdlde_hexencoder.h:622
HexEncoder(bool upperCaseLetters=true)
~HexEncoder()=default
Destroy this object.
bool isInitialState() const
Definition bdlde_hexencoder.h:634
void reset()
Definition bdlde_hexencoder.h:607
int numOutputPending() const
Definition bdlde_hexencoder.h:646
int endConvert(OUTPUT_ITERATOR out)
Definition bdlde_hexencoder.h:568
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bdlde_base64alphabet.h:118