diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-11-06 16:20:21 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-11-11 10:08:08 +0100 |
commit | 66659ff1ad6831b0ea7425fa6ecd8a8687523658 (patch) | |
tree | 48e22b475fa8854128e0861a33fed6f78c8094b5 /freebsd/sys/kern/subr_sbuf.c | |
parent | Define __GLOBL1() and __GLOBL() (diff) | |
download | rtems-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.c | 314 |
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); +} |