summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/kern/subr_sbuf.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2013-11-06 16:20:21 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2013-11-11 10:08:08 +0100
commit66659ff1ad6831b0ea7425fa6ecd8a8687523658 (patch)
tree48e22b475fa8854128e0861a33fed6f78c8094b5 /freebsd/sys/kern/subr_sbuf.c
parentDefine __GLOBL1() and __GLOBL() (diff)
downloadrtems-libbsd-66659ff1ad6831b0ea7425fa6ecd8a8687523658.tar.bz2
Update to FreeBSD 9.2
Diffstat (limited to 'freebsd/sys/kern/subr_sbuf.c')
-rw-r--r--freebsd/sys/kern/subr_sbuf.c314
1 files changed, 191 insertions, 123 deletions
diff --git a/freebsd/sys/kern/subr_sbuf.c b/freebsd/sys/kern/subr_sbuf.c
index a92c09c1..9ea11990 100644
--- a/freebsd/sys/kern/subr_sbuf.c
+++ b/freebsd/sys/kern/subr_sbuf.c
@@ -52,12 +52,6 @@ __FBSDID("$FreeBSD$");
#include <sys/sbuf.h>
-struct sbuf_drain {
- sbuf_drain_func *s_func; /* drain function */
- void *s_arg; /* user-supplied drain argument */
- int s_error; /* current error code */
-};
-
#ifdef _KERNEL
static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
#define SBMALLOC(size) malloc(size, M_SBUF, M_WAITOK)
@@ -74,10 +68,10 @@ static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
#define SBUF_ISDYNAMIC(s) ((s)->s_flags & SBUF_DYNAMIC)
#define SBUF_ISDYNSTRUCT(s) ((s)->s_flags & SBUF_DYNSTRUCT)
#define SBUF_ISFINISHED(s) ((s)->s_flags & SBUF_FINISHED)
-#define SBUF_HASOVERFLOWED(s) ((s)->s_flags & SBUF_OVERFLOWED)
#define SBUF_HASROOM(s) ((s)->s_len < (s)->s_size - 1)
-#define SBUF_FREESPACE(s) ((s)->s_size - (s)->s_len - 1)
+#define SBUF_FREESPACE(s) ((s)->s_size - ((s)->s_len + 1))
#define SBUF_CANEXTEND(s) ((s)->s_flags & SBUF_AUTOEXTEND)
+#define SBUF_ISSECTION(s) ((s)->s_flags & SBUF_INSECTION)
/*
* Set / clear flags
@@ -86,8 +80,14 @@ static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
#define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0)
#define SBUF_MINEXTENDSIZE 16 /* Should be power of 2. */
+
+#ifdef PAGE_SIZE
#define SBUF_MAXEXTENDSIZE PAGE_SIZE
#define SBUF_MAXEXTENDINCR PAGE_SIZE
+#else
+#define SBUF_MAXEXTENDSIZE 4096
+#define SBUF_MAXEXTENDINCR 4096
+#endif
/*
* Debugging support
@@ -103,7 +103,8 @@ _assert_sbuf_integrity(const char *fun, struct sbuf *s)
KASSERT(s->s_buf != NULL,
("%s called with uninitialized or corrupt sbuf", fun));
KASSERT(s->s_len < s->s_size,
- ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
+ ("wrote past end of sbuf (%jd >= %jd)",
+ (intmax_t)s->s_len, (intmax_t)s->s_size));
}
static void
@@ -146,7 +147,6 @@ sbuf_extendsize(int size)
return (newsize);
}
-
/*
* Extend an sbuf.
*/
@@ -162,7 +162,7 @@ sbuf_extend(struct sbuf *s, int addlen)
newbuf = SBMALLOC(newsize);
if (newbuf == NULL)
return (-1);
- bcopy(s->s_buf, newbuf, s->s_size);
+ memcpy(newbuf, s->s_buf, s->s_size);
if (SBUF_ISDYNAMIC(s))
SBFREE(s->s_buf);
else
@@ -173,6 +173,38 @@ sbuf_extend(struct sbuf *s, int addlen)
}
/*
+ * Initialize the internals of an sbuf.
+ * If buf is non-NULL, it points to a static or already-allocated string
+ * big enough to hold at least length characters.
+ */
+static struct sbuf *
+sbuf_newbuf(struct sbuf *s, char *buf, int length, int flags)
+{
+
+ memset(s, 0, sizeof(*s));
+ s->s_flags = flags;
+ s->s_size = length;
+ s->s_buf = buf;
+
+ if ((s->s_flags & SBUF_AUTOEXTEND) == 0) {
+ KASSERT(s->s_size >= 0,
+ ("attempt to create a too small sbuf"));
+ }
+
+ if (s->s_buf != NULL)
+ return (s);
+
+ if ((flags & SBUF_AUTOEXTEND) != 0)
+ s->s_size = sbuf_extendsize(s->s_size);
+
+ s->s_buf = SBMALLOC(s->s_size);
+ if (s->s_buf == NULL)
+ return (NULL);
+ SBUF_SETFLAG(s, SBUF_DYNAMIC);
+ return (s);
+}
+
+/*
* Initialize an sbuf.
* If buf is non-NULL, it points to a static or already-allocated string
* big enough to hold at least length characters.
@@ -187,31 +219,17 @@ sbuf_new(struct sbuf *s, char *buf, int length, int flags)
("%s called with invalid flags", __func__));
flags &= SBUF_USRFLAGMSK;
- if (s == NULL) {
- s = SBMALLOC(sizeof(*s));
- if (s == NULL)
- return (NULL);
- bzero(s, sizeof(*s));
- s->s_flags = flags;
- SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
- } else {
- bzero(s, sizeof(*s));
- s->s_flags = flags;
- }
- s->s_size = length;
- if (buf != NULL) {
- s->s_buf = buf;
- return (s);
- }
- if ((flags & SBUF_AUTOEXTEND) != 0)
- s->s_size = sbuf_extendsize(s->s_size);
- s->s_buf = SBMALLOC(s->s_size);
- if (s->s_buf == NULL) {
- if (SBUF_ISDYNSTRUCT(s))
- SBFREE(s);
+ if (s != NULL)
+ return (sbuf_newbuf(s, buf, length, flags));
+
+ s = SBMALLOC(sizeof(*s));
+ if (s == NULL)
+ return (NULL);
+ if (sbuf_newbuf(s, buf, length, flags) == NULL) {
+ SBFREE(s);
return (NULL);
}
- SBUF_SETFLAG(s, SBUF_DYNAMIC);
+ SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
return (s);
}
@@ -239,6 +257,8 @@ sbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
return (NULL);
}
s->s_len = s->s_size - 1;
+ if (SBUF_ISSECTION(s))
+ s->s_sect_len = s->s_size - 1;
*error = 0;
return (s);
}
@@ -255,10 +275,9 @@ sbuf_clear(struct sbuf *s)
/* don't care if it's finished or not */
SBUF_CLEARFLAG(s, SBUF_FINISHED);
- SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
- if (s->s_drain != NULL)
- s->s_drain->s_error = 0;
+ s->s_error = 0;
s->s_len = 0;
+ s->s_sect_len = 0;
}
/*
@@ -266,16 +285,19 @@ sbuf_clear(struct sbuf *s)
* Effectively truncates the sbuf at the new position.
*/
int
-sbuf_setpos(struct sbuf *s, int pos)
+sbuf_setpos(struct sbuf *s, ssize_t pos)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
KASSERT(pos >= 0,
- ("attempt to seek to a negative position (%d)", pos));
+ ("attempt to seek to a negative position (%jd)", (intmax_t)pos));
KASSERT(pos < s->s_size,
- ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
+ ("attempt to seek past end of sbuf (%jd >= %jd)",
+ (intmax_t)pos, (intmax_t)s->s_size));
+ KASSERT(!SBUF_ISSECTION(s),
+ ("attempt to seek when in a section"));
if (pos < 0 || pos > s->s_len)
return (-1);
@@ -293,22 +315,10 @@ sbuf_set_drain(struct sbuf *s, sbuf_drain_func *func, void *ctx)
assert_sbuf_state(s, 0);
assert_sbuf_integrity(s);
- KASSERT((s->s_drain != NULL && func == s->s_drain->s_func) ||
- s->s_len == 0,
+ KASSERT(func == s->s_drain_func || s->s_len == 0,
("Cannot change drain to %p on non-empty sbuf %p", func, s));
- if (func == NULL) {
- SBFREE(s->s_drain);
- s->s_drain = NULL;
- return;
- }
- if (s->s_drain == NULL) {
- s->s_drain = SBMALLOC(sizeof(*s->s_drain));
- if (s->s_drain == NULL)
- return;
- }
- s->s_drain->s_func = func;
- s->s_drain->s_arg = ctx;
- s->s_drain->s_error = 0;
+ s->s_drain_func = func;
+ s->s_drain_arg = ctx;
}
/*
@@ -320,11 +330,11 @@ sbuf_drain(struct sbuf *s)
int len;
KASSERT(s->s_len > 0, ("Shouldn't drain empty sbuf %p", s));
- len = s->s_drain->s_func(s->s_drain->s_arg, s->s_buf, s->s_len);
+ KASSERT(s->s_error == 0, ("Called %s with error on %p", __func__, s));
+ len = s->s_drain_func(s->s_drain_arg, s->s_buf, s->s_len);
if (len < 0) {
- s->s_drain->s_error = -len;
- SBUF_SETFLAG(s, SBUF_OVERFLOWED);
- return (s->s_drain->s_error);
+ s->s_error = -len;
+ return (s->s_error);
}
KASSERT(len > 0 && len <= s->s_len,
("Bad drain amount %d for sbuf %p", len, s));
@@ -349,39 +359,29 @@ sbuf_drain(struct sbuf *s)
* buffer and marking overflow.
*/
static void
-sbuf_put_byte(int c, struct sbuf *s)
+sbuf_put_byte(struct sbuf *s, int c)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
- if (SBUF_HASOVERFLOWED(s))
+ if (s->s_error != 0)
return;
if (SBUF_FREESPACE(s) <= 0) {
- /*
+ /*
* If there is a drain, use it, otherwise extend the
* buffer.
*/
- if (s->s_drain != NULL)
+ if (s->s_drain_func != NULL)
(void)sbuf_drain(s);
else if (sbuf_extend(s, 1) < 0)
- SBUF_SETFLAG(s, SBUF_OVERFLOWED);
- if (SBUF_HASOVERFLOWED(s))
+ s->s_error = ENOMEM;
+ if (s->s_error != 0)
return;
}
s->s_buf[s->s_len++] = c;
-}
-
-/*
- * Append a non-NUL character to an sbuf. This prototype signature is
- * suitable for use with kvprintf(9).
- */
-static void
-sbuf_putc_func(int c, void *arg)
-{
-
- if (c != '\0')
- sbuf_put_byte(c, arg);
+ if (SBUF_ISSECTION(s))
+ s->s_sect_len++;
}
/*
@@ -396,13 +396,13 @@ sbuf_bcat(struct sbuf *s, const void *buf, size_t len)
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
- if (SBUF_HASOVERFLOWED(s))
+ if (s->s_error != 0)
return (-1);
for (; str < end; str++) {
- sbuf_put_byte(*str, s);
- if (SBUF_HASOVERFLOWED(s))
+ sbuf_put_byte(s, *str);
+ if (s->s_error != 0)
return (-1);
- }
+ }
return (0);
}
@@ -416,10 +416,10 @@ sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
- KASSERT(s->s_drain == NULL,
+ KASSERT(s->s_drain_func == NULL,
("Nonsensical copyin to sbuf %p with a drain", s));
- if (SBUF_HASOVERFLOWED(s))
+ if (s->s_error != 0)
return (-1);
if (len == 0)
return (0);
@@ -460,12 +460,12 @@ sbuf_cat(struct sbuf *s, const char *str)
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
- if (SBUF_HASOVERFLOWED(s))
+ if (s->s_error != 0)
return (-1);
while (*str != '\0') {
- sbuf_put_byte(*str++, s);
- if (SBUF_HASOVERFLOWED(s))
+ sbuf_put_byte(s, *str++);
+ if (s->s_error != 0)
return (-1);
}
return (0);
@@ -482,10 +482,10 @@ sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
- KASSERT(s->s_drain == NULL,
+ KASSERT(s->s_drain_func == NULL,
("Nonsensical copyin to sbuf %p with a drain", s));
- if (SBUF_HASOVERFLOWED(s))
+ if (s->s_error != 0)
return (-1);
if (len == 0)
@@ -497,10 +497,12 @@ sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
}
switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) {
case ENAMETOOLONG:
- SBUF_SETFLAG(s, SBUF_OVERFLOWED);
+ s->s_error = ENOMEM;
/* fall through */
case 0:
s->s_len += done - 1;
+ if (SBUF_ISSECTION(s))
+ s->s_sect_len += done - 1;
break;
default:
return (-1); /* XXX */
@@ -528,6 +530,19 @@ sbuf_cpy(struct sbuf *s, const char *str)
* Format the given argument list and append the resulting string to an sbuf.
*/
#if defined(_KERNEL) && !defined(__rtems__)
+
+/*
+ * Append a non-NUL character to an sbuf. This prototype signature is
+ * suitable for use with kvprintf(9).
+ */
+static void
+sbuf_putc_func(int c, void *arg)
+{
+
+ if (c != '\0')
+ sbuf_put_byte(arg, c);
+}
+
int
sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
{
@@ -539,7 +554,7 @@ sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
("%s called with a NULL format string", __func__));
(void)kvprintf(fmt, sbuf_putc_func, s, 10, ap);
- if (SBUF_HASOVERFLOWED(s))
+ if (s->s_error != 0)
return (-1);
return (0);
}
@@ -556,7 +571,7 @@ sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
KASSERT(fmt != NULL,
("%s called with a NULL format string", __func__));
- if (SBUF_HASOVERFLOWED(s))
+ if (s->s_error != 0)
return (-1);
/*
@@ -580,7 +595,7 @@ sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
if (SBUF_FREESPACE(s) >= len)
break;
/* Cannot print with the current available space. */
- if (s->s_drain != NULL && s->s_len > 0)
+ if (s->s_drain_func != NULL && s->s_len > 0)
error = sbuf_drain(s);
else
error = sbuf_extend(s, len - SBUF_FREESPACE(s));
@@ -598,13 +613,15 @@ sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
if (SBUF_FREESPACE(s) < len)
len = SBUF_FREESPACE(s);
s->s_len += len;
+ if (SBUF_ISSECTION(s))
+ s->s_sect_len += len;
if (!SBUF_HASROOM(s) && !SBUF_CANEXTEND(s))
- SBUF_SETFLAG(s, SBUF_OVERFLOWED);
+ s->s_error = ENOMEM;
KASSERT(s->s_len < s->s_size,
("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
- if (SBUF_HASOVERFLOWED(s))
+ if (s->s_error != 0)
return (-1);
return (0);
}
@@ -632,8 +649,8 @@ int
sbuf_putc(struct sbuf *s, int c)
{
- sbuf_putc_func(c, s);
- if (SBUF_HASOVERFLOWED(s))
+ sbuf_put_byte(s, c);
+ if (s->s_error != 0)
return (-1);
return (0);
}
@@ -647,26 +664,29 @@ sbuf_trim(struct sbuf *s)
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
- KASSERT(s->s_drain == NULL,
+ KASSERT(s->s_drain_func == NULL,
("%s makes no sense on sbuf %p with drain", __func__, s));
- if (SBUF_HASOVERFLOWED(s))
+ if (s->s_error != 0)
return (-1);
- while (s->s_len > 0 && isspace(s->s_buf[s->s_len-1]))
+ while (s->s_len > 0 && isspace(s->s_buf[s->s_len-1])) {
--s->s_len;
+ if (SBUF_ISSECTION(s))
+ s->s_sect_len--;
+ }
return (0);
}
/*
- * Check if an sbuf overflowed
+ * Check if an sbuf has an error.
*/
int
-sbuf_overflowed(struct sbuf *s)
+sbuf_error(const struct sbuf *s)
{
- return (SBUF_HASOVERFLOWED(s));
+ return (s->s_error);
}
/*
@@ -675,28 +695,23 @@ sbuf_overflowed(struct sbuf *s)
int
sbuf_finish(struct sbuf *s)
{
- int error = 0;
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
- if (s->s_drain != NULL) {
- error = s->s_drain->s_error;
- while (s->s_len > 0 && error == 0)
- error = sbuf_drain(s);
- } else if (SBUF_HASOVERFLOWED(s))
- error = ENOMEM;
+ if (s->s_drain_func != NULL) {
+ while (s->s_len > 0 && s->s_error == 0)
+ s->s_error = sbuf_drain(s);
+ }
s->s_buf[s->s_len] = '\0';
- SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
SBUF_SETFLAG(s, SBUF_FINISHED);
#ifdef _KERNEL
- return (error);
+ return (s->s_error);
#else
- /*XXX*/if (error) {
- errno = error;
+ errno = s->s_error;
+ if (s->s_error)
return (-1);
- } else
- return (0);
+ return (0);
#endif
}
@@ -709,7 +724,7 @@ sbuf_data(struct sbuf *s)
assert_sbuf_integrity(s);
assert_sbuf_state(s, SBUF_FINISHED);
- KASSERT(s->s_drain == NULL,
+ KASSERT(s->s_drain_func == NULL,
("%s makes no sense on sbuf %p with drain", __func__, s));
return (s->s_buf);
@@ -718,16 +733,16 @@ sbuf_data(struct sbuf *s)
/*
* Return the length of the sbuf data.
*/
-int
+ssize_t
sbuf_len(struct sbuf *s)
{
assert_sbuf_integrity(s);
/* don't care if it's finished or not */
- KASSERT(s->s_drain == NULL,
+ KASSERT(s->s_drain_func == NULL,
("%s makes no sense on sbuf %p with drain", __func__, s));
- if (SBUF_HASOVERFLOWED(s))
+ if (s->s_error != 0)
return (-1);
return (s->s_len);
}
@@ -745,10 +760,8 @@ sbuf_delete(struct sbuf *s)
if (SBUF_ISDYNAMIC(s))
SBFREE(s->s_buf);
- if (s->s_drain != NULL)
- SBFREE(s->s_drain);
isdyn = SBUF_ISDYNSTRUCT(s);
- bzero(s, sizeof(*s));
+ memset(s, 0, sizeof(*s));
if (isdyn)
SBFREE(s);
}
@@ -757,8 +770,63 @@ sbuf_delete(struct sbuf *s)
* Check if an sbuf has been finished.
*/
int
-sbuf_done(struct sbuf *s)
+sbuf_done(const struct sbuf *s)
{
return (SBUF_ISFINISHED(s));
}
+
+/*
+ * Start a section.
+ */
+void
+sbuf_start_section(struct sbuf *s, ssize_t *old_lenp)
+{
+
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ if (!SBUF_ISSECTION(s)) {
+ KASSERT(s->s_sect_len == 0,
+ ("s_sect_len != 0 when starting a section"));
+ if (old_lenp != NULL)
+ *old_lenp = -1;
+ SBUF_SETFLAG(s, SBUF_INSECTION);
+ } else {
+ KASSERT(old_lenp != NULL,
+ ("s_sect_len should be saved when starting a subsection"));
+ *old_lenp = s->s_sect_len;
+ s->s_sect_len = 0;
+ }
+}
+
+/*
+ * End the section padding to the specified length with the specified
+ * character.
+ */
+ssize_t
+sbuf_end_section(struct sbuf *s, ssize_t old_len, size_t pad, int c)
+{
+ ssize_t len;
+
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+ KASSERT(SBUF_ISSECTION(s),
+ ("attempt to end a section when not in a section"));
+
+ if (pad > 1) {
+ len = roundup(s->s_sect_len, pad) - s->s_sect_len;
+ for (; s->s_error == 0 && len > 0; len--)
+ sbuf_put_byte(s, c);
+ }
+ len = s->s_sect_len;
+ if (old_len == -1) {
+ s->s_sect_len = 0;
+ SBUF_CLEARFLAG(s, SBUF_INSECTION);
+ } else {
+ s->s_sect_len += old_len;
+ }
+ if (s->s_error != 0)
+ return (-1);
+ return (len);
+}