/** Refresh the buffer with more data from the file
*
*/
-size_t fr_sbuff_extend_file(fr_sbuff_t *sbuff, size_t extension)
+size_t fr_sbuff_extend_file(fr_sbuff_extend_status_t *status, fr_sbuff_t *sbuff, size_t extension)
{
fr_sbuff_t *sbuff_i;
size_t read, available, total_read, shift;
if (read < available) {
if (!feof(fctx->file)) { /* It's a real error */
fr_strerror_printf("Error extending buffer: %s", fr_syserror(ferror(fctx->file)));
+ *status |= FR_SBUFF_FLAG_EXTEND_ERROR;
return 0;
}
return read;
}
+/** Accessor function for the EOF state of the file extendor
+ *
+ */
+bool fr_sbuff_eof_file(fr_sbuff_t *sbuff)
+{
+ fr_sbuff_uctx_file_t *fctx = sbuff->uctx;
+ return fctx->eof;
+}
+
/** Reallocate the current buffer
*
+ * @param[in] status Extend status.
* @param[in] sbuff to be extended.
* @param[in] extension How many additional bytes should be allocated
* in the buffer.
* - 0 the extension operation failed.
* - >0 the number of bytes the buffer was extended by.
*/
-size_t fr_sbuff_extend_talloc(fr_sbuff_t *sbuff, size_t extension)
+size_t fr_sbuff_extend_talloc(fr_sbuff_extend_status_t *status, fr_sbuff_t *sbuff, size_t extension)
{
fr_sbuff_uctx_talloc_t *tctx = sbuff->uctx;
size_t clen, nlen, elen = extension;
new_buff = talloc_realloc(tctx->ctx, sbuff->buff, char, nlen);
if (unlikely(!new_buff)) {
fr_strerror_printf("Failed extending buffer by %zu bytes to %zu bytes", elen, nlen);
+ *status |= FR_SBUFF_FLAG_EXTEND_ERROR;
return 0;
}
ssize_t mid;
size_t remaining;
- bool reset_p = (p == in->p);
- fr_sbuff_extend_status_t status = FR_SBUFF_EXTENDABLE;
if (!term) return false; /* If there's no terminals, we don't need to search */
/*
* Special case for EOFlike states
*/
- remaining = fr_sbuff_extend_lowat(&status, in, needle_len);
- if (remaining == 0) {
- if (status & FR_SBUFF_EXTEND_ERROR) return false;
- return (idx['\0'] != 0);
+ remaining = fr_sbuff_remaining(in);
+ if ((remaining == 0) && !fr_sbuff_is_extendable(in)) {
+ if (idx['\0'] != 0) return true;
+ return false;
}
- if (reset_p) p = in->p;
+ if (remaining < needle_len) {
+ fr_assert_msg(!fr_sbuff_is_extendable(in),
+ "Caller failed to extend buffer by %zu bytes before calling fr_sbuff_terminal_search",
+ needle_len);
+ /*
+ * We can't search for the needle if we don't have
+ * enough data to match it.
+ */
+ return false;
+ }
mid = term_idx - 1; /* Inform the mid point from the index */
uint8_t idx[UINT8_MAX + 1]; /* Fast path index */
size_t needle_len = 1;
-
- fr_sbuff_extend_status_t status = FR_SBUFF_EXTENDABLE; /* Tracks if we can extend */
+ fr_sbuff_extend_status_t status = 0;
/*
* If we don't need to do unescaping
* No terminal, check for EOF.
*/
if (!tt) {
- fr_sbuff_extend_status_t status = FR_SBUFF_EXTENDABLE;
+ fr_sbuff_extend_status_t status = 0;
if ((fr_sbuff_extend_lowat(&status, in, 1) == 0) &&
- (status & FR_SBUFF_EXTEND_ERROR) == 0) {
+ (status & FR_SBUFF_FLAG_EXTEND_ERROR) == 0) {
return true;
}
*/
fr_sbuff_terminal_idx_init(&needle_len, idx, tt);
+ fr_sbuff_extend_lowat(NULL, in, needle_len);
+
return fr_sbuff_terminal_search(in, in->p, idx, tt, needle_len);
}
typedef ssize_t fr_slen_t;
typedef struct fr_sbuff_s fr_sbuff_t;
typedef struct fr_sbuff_ptr_s fr_sbuff_marker_t;
-typedef size_t(*fr_sbuff_extend_t)(fr_sbuff_t *sbuff, size_t req_extension);
#include <freeradius-devel/util/atexit.h>
#include <freeradius-devel/util/strerror.h>
#include <freeradius-devel/util/table.h>
#include <freeradius-devel/util/talloc.h>
+/** Whether the buffer is currently extendable and whether it was extended
+ *
+ */
+DIAG_OFF(attributes)
+typedef enum CC_HINT(flag_enum) {
+ FR_SBUFF_FLAG_EXTENDED = 0x01, //!< The last call to extend function actually extended the buffer.
+ FR_SBUFF_FLAG_EXTEND_ERROR = 0x02 //!< The last call to an extend function resulted in an error.
+ ///< Error should be provided using fr_strerror_const/fr_strerror_printf
+ ///< by the extension function.
+} fr_sbuff_extend_status_t;
+DIAG_OFF(attributes)
+
+/** Extension callback
+ *
+ * Retrieves additional data from a source and adds it to a buffer.
+ */
+typedef size_t(*fr_sbuff_extend_t)(fr_sbuff_extend_status_t *status, fr_sbuff_t *sbuff, size_t req_extension);
+
+/** For a given extension function, returns whether it is at EOF
+ *
+ */
+typedef bool(*fr_sbuff_eof_t)(fr_sbuff_t *sbuff);
+
+
struct fr_sbuff_ptr_s {
union {
char const *p_i; //!< Immutable position pointer.
fr_sbuff_extend_t extend; //!< Function to re-populate or extend
///< the buffer.
+ fr_sbuff_eof_t eof; //!< Function to determine if the buffer is at EOF.
+
void *uctx; //!< Extend uctx data.
fr_sbuff_t *parent; //!< sbuff this sbuff was copied from.
fr_strerror_const(fr_table_str_by_value(sbuff_parse_error_table, err, "<INVALID>"));
}
-#define FR_SBUFF_FLAG_EXTENDABLE 0x01
-#define FR_SBUFF_FLAG_EXTENDED 0x02
-#define FR_SBUFF_FLAG_EXTEND_ERROR 0x04
-
-/** Whether the buffer is currently extendable and whether it was extended
- *
+/** Return whether the sbuff is extendable
*/
-typedef enum {
- FR_SBUFF_NOT_EXTENDABLE = 0x00,
- FR_SBUFF_EXTENDABLE = FR_SBUFF_FLAG_EXTENDABLE,
- FR_SBUFF_EXTENDABLE_EXTENDED = FR_SBUFF_FLAG_EXTENDABLE | FR_SBUFF_FLAG_EXTENDED,
- FR_SBUFF_EXTENDED = FR_SBUFF_FLAG_EXTENDED,
- FR_SBUFF_EXTEND_ERROR = FR_SBUFF_FLAG_EXTEND_ERROR
-} fr_sbuff_extend_status_t;
+static inline bool fr_sbuff_is_extendable(fr_sbuff_t *sbuff)
+{
+ return sbuff->extend && (!sbuff->eof || (sbuff->eof(sbuff) == false));
+}
-#define fr_sbuff_is_extendable(_status) ((_status) & FR_SBUFF_FLAG_EXTENDABLE)
-#define fr_sbuff_was_extended(_status) ((_status) & FR_SBUFF_FLAG_EXTENDED)
+#define fr_sbuff_was_extended(_status) (_status & FR_SBUFF_FLAG_EXTENDED)
extern bool const sbuff_char_class_uint[UINT8_MAX + 1];
extern bool const sbuff_char_class_int[UINT8_MAX + 1];
*
* @private
*/
-#define _FR_SBUFF(_sbuff_or_marker, _start, _current, _end, _extend, _adv_parent) \
+#define _FR_SBUFF(_sbuff_or_marker, _start, _current, _end, _extend, _eof, _adv_parent) \
((fr_sbuff_t){ \
.buff = fr_sbuff_buff(_sbuff_or_marker), \
.start = (_start), \
.adv_parent = (_adv_parent), \
.shifted = 0, \
.extend = (_extend), \
+ .eof = (_eof), \
.uctx = fr_sbuff_ptr(_sbuff_or_marker)->uctx, \
.parent = fr_sbuff_ptr(_sbuff_or_marker) \
})
fr_sbuff_current(_sbuff_or_marker), \
fr_sbuff_end(_sbuff_or_marker), \
fr_sbuff_ptr(_sbuff_or_marker)->extend, \
+ fr_sbuff_ptr(_sbuff_or_marker)->eof, \
0x00)
/** Create a new sbuff pointing to the same underlying buffer
fr_sbuff_start(_sbuff_or_marker), \
fr_sbuff_current(_sbuff_or_marker), \
NULL, \
+ NULL, \
0x00)
/** Create a new sbuff pointing to the same underlying buffer
fr_sbuff_current(_sbuff_or_marker), \
fr_sbuff_end(_sbuff_or_marker), \
fr_sbuff_ptr(_sbuff_or_marker)->extend, \
+ fr_sbuff_ptr(_sbuff_or_marker)->eof, \
0x01)
/** Create a new sbuff pointing to the same underlying buffer
fr_sbuff_current(_sbuff_or_marker), \
fr_sbuff_end(_sbuff_or_marker), \
fr_sbuff_ptr(_sbuff_or_marker)->extend, \
+ fr_sbuff_ptr(_sbuff_or_marker)->eof, \
0x01)
/** Creates a compound literal to pass into functions which accept a sbuff
size_t fr_sbuff_shift(fr_sbuff_t *sbuff, size_t shift);
-size_t fr_sbuff_extend_file(fr_sbuff_t *sbuff, size_t extension);
+size_t fr_sbuff_extend_file(fr_sbuff_extend_status_t *status, fr_sbuff_t *sbuff, size_t extension);
+
+bool fr_sbuff_eof_file(fr_sbuff_t *sbuff);
-size_t fr_sbuff_extend_talloc(fr_sbuff_t *sbuff, size_t extension);
+size_t fr_sbuff_extend_talloc(fr_sbuff_extend_status_t *status, fr_sbuff_t *sbuff, size_t extension);
int fr_sbuff_trim_talloc(fr_sbuff_t *sbuff, size_t len);
.p = buff,
.end = buff, //!< Starts with 0 bytes available
.extend = fr_sbuff_extend_file,
+ .eof = fr_sbuff_eof_file,
.uctx = fctx
};
#define FR_SBUFF_CHECK_REMAINING_RETURN(_sbuff, _len) \
if ((_len) > fr_sbuff_remaining(_sbuff)) return -((_len) - fr_sbuff_remaining(_sbuff))
-static inline size_t _fr_sbuff_extend_lowat(fr_sbuff_extend_status_t *status, fr_sbuff_t *in,
- size_t remaining, size_t lowat)
+static inline size_t _fr_sbuff_extend_lowat(fr_sbuff_extend_status_t *status, fr_sbuff_t *in, size_t remaining, size_t lowat)
{
size_t extended;
+ fr_sbuff_extend_status_t our_status = 0;
- if (status && !(*status & FR_SBUFF_EXTENDABLE)) {
- not_extendable:
- if (status) *status = FR_SBUFF_NOT_EXTENDABLE;
+ if (!fr_sbuff_is_extendable(in)) {
+ no_extend:
+ if (status) *status = our_status;
return remaining;
}
- if (remaining >= lowat) {
- if (status) *status = FR_SBUFF_EXTENDABLE;
- return remaining;
- }
+ /* Still have data remaining, no need to try and extend */
+ if (remaining >= lowat) goto no_extend;
- if (!in->extend || !(extended = in->extend(in, lowat - remaining))) goto not_extendable;
+ if (!in->extend || ((extended = in->extend(&our_status, in, lowat - remaining)) == 0)) {
+ goto no_extend;
+ }
- if (status) *status = FR_SBUFF_EXTENDABLE_EXTENDED;
+ our_status |= FR_SBUFF_FLAG_EXTENDED;
+ if (status) *status = our_status;
return remaining + extended;
}