/* * MBuffer module * See "mbuffer.h" for the details. * * Copyright INOUE Seiichiro , licensed under the GPL. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if defined(HAVE_STRING_H) #include #elif defined(HAVE_STRINGS_H) #include #endif #include #include "mbuffer.h" /* Data structure definitions */ struct _MBufferPrivate { const char *buf;/* pointer to static buffer, which is not null-byte terminated */ GHashTable *cache_ht;/* key: line number value: pointer to the head of the line */ }; /* Private function declarations */ static const char* skip_n_lines(const char *ptr, int nl, int lenb); MBuffer* mbuf_new(const char *buf, int lenb, int nl, gboolean f_cache) { MBuffer *mbuf; mbuf = g_new(MBuffer, 1); _mbuf_init(mbuf, buf, lenb, nl, f_cache); return mbuf; } void mbuf_delete(MBuffer *mbuf) { g_return_if_fail(mbuf); _mbuf_finalize(mbuf); g_free(mbuf); } /* Called from a derived type */ void _mbuf_init(MBuffer *mbuf, const char *buf, int lenb, int nl, gboolean f_cache) { MBufferPrivate *privat; mbuf->cur_ln = 1; mbuf->cur_pt = buf; mbuf->lenb = lenb; mbuf->nl = nl; privat = g_new(MBufferPrivate, 1); mbuf->privat = privat; privat->buf = buf; if (f_cache) privat->cache_ht = g_hash_table_new(g_direct_hash, g_direct_equal); else privat->cache_ht = NULL; } /* Called from a derived type */ void _mbuf_finalize(MBuffer *mbuf) { if (mbuf->privat->cache_ht) g_hash_table_destroy(mbuf->privat->cache_ht); } /** * mbuf_goto_line: * Goto the specified line @ln, and return the pointer of the head of the line. * Also update internal data. **/ const char* mbuf_goto_line(MBuffer *mbuf, int ln) { MBufferPrivate *privat = mbuf->privat; const char *cur_pt = mbuf->cur_pt; int left_lenb = mbuf->lenb - (cur_pt - privat->buf); const char *pt; g_return_val_if_fail(ln >= 0, NULL); g_return_val_if_fail(mbuf && ln <= (mbuf->nl + 1), NULL); /* In some cases, it can access quickly. */ if (ln == 0 || ln == 1) {/* the top of the buffer */ mbuf->cur_ln = 1; mbuf->cur_pt = privat->buf; return mbuf->cur_pt; } else if (ln == (mbuf->nl + 1)) {/* the bottom of the buffer */ mbuf->cur_ln = mbuf->nl + 1; mbuf->cur_pt = mbuf->privat->buf + mbuf->lenb; return mbuf->cur_pt; } /* At first, look up the cache */ if (privat->cache_ht) { pt = g_hash_table_lookup(privat->cache_ht, GINT_TO_POINTER(ln)); if (pt) { #ifdef CACHE_DEBUG /* This cache is working? I'm not sure. */ g_print("mbuf_goto_line: cache hit\n"); #endif mbuf->cur_ln = ln; mbuf->cur_pt = pt; return mbuf->cur_pt; } } if (ln > mbuf->cur_ln) { mbuf->cur_pt = skip_n_lines(cur_pt, ln - mbuf->cur_ln, left_lenb); if (mbuf->cur_pt == NULL) {/* Exceed, but legal */ g_assert(ln == (mbuf->nl + 1)); mbuf->cur_pt = mbuf->privat->buf + mbuf->lenb; } } else if (ln < mbuf->cur_ln) { /* mbuf_prev_line() could be more efficient, but rewind it now. */ mbuf_goto_top(mbuf); #ifdef DEBUG g_print("mbuf rewind occurs\n"); #endif mbuf_goto_line(mbuf, ln); } mbuf->cur_ln = ln; if (privat->cache_ht) { g_hash_table_insert(privat->cache_ht, GINT_TO_POINTER(mbuf->cur_ln), (gpointer)mbuf->cur_pt); } return mbuf->cur_pt; } /** * mbuf_next_line: * Go ahead the specified number of lines @nl, and return the pointer * of the head of the line. * Also update internal data. **/ const char* mbuf_next_line(MBuffer *mbuf, int nl) { MBufferPrivate *privat = mbuf->privat; const char *cur_pt = mbuf->cur_pt; int left_lenb = mbuf->lenb - (cur_pt - privat->buf); const char *pt; g_return_val_if_fail(mbuf && (mbuf->cur_ln + nl) <= (mbuf->nl + 1), NULL); /* At first, look up the cache */ if (privat->cache_ht) { pt = g_hash_table_lookup(privat->cache_ht, GINT_TO_POINTER(mbuf->cur_ln + nl)); if (pt) { #ifdef CACHE_DEBUG /* This cache is working? I'm not sure. */ g_print("mbuf_next_line: cache hit\n"); #endif mbuf->cur_ln += nl; mbuf->cur_pt = pt; return mbuf->cur_pt; } } mbuf->cur_pt = skip_n_lines(cur_pt, nl, left_lenb); if (mbuf->cur_pt == NULL) {/* Exceed, but legal */ g_assert((mbuf->cur_ln + nl) == (mbuf->nl + 1)); mbuf->cur_pt = mbuf->privat->buf + mbuf->lenb; } mbuf->cur_ln += nl; /* Don't insert this to cache table, because this could be called so frequently. */ return mbuf->cur_pt; } /** * mbuf_prev_line: * Not implemented. * Go back the specified number of lines @nl, and return the pointer * of the head of the line. * Also update internal data. **/ const char* mbuf_prev_line(MBuffer *mbuf, int nl) { g_warning("not implemented yet\n"); g_return_val_if_fail(mbuf && (mbuf->cur_ln - nl) > 0, NULL); return mbuf->cur_pt; } /** * mbuf_search_string: * Search specified string in mbuf, and return line number where it is found. * Input: * int start_ln; line number from which search starts. * const char *string; search string. * int lenb; byte-length of @string. * Output: * Return value; line number where @string found. zero means not-found. **/ int mbuf_search_string(MBuffer *mbuf, int start_ln, const char *string, int lenb) { MBufferPrivate *privat = mbuf->privat; const char *pt; int left_lenb; int ret_ln; mbuf_goto_line(mbuf, start_ln); ret_ln = (start_ln == 0) ? 1 : start_ln; pt = mbuf->cur_pt; left_lenb = mbuf->lenb - (pt - privat->buf); for (; left_lenb; pt++, left_lenb--) { if (*pt == '\n') ret_ln++; if (*string == *pt) if (strncmp(pt, string, lenb) == 0) return ret_ln; } return 0; } /* ---The followings are private functions--- */ /** * skip_n_lines: * Skip @nl lines in buffer, and return the head of the line. * Input: * const char *ptr; Buffer. Not null-byte terminated. * int nl; The number of lines to skip * int lenb; buffer length(byte) * Output: * Return value; Pointer to the head of the line. If exceeds, NULL. **/ static const char* skip_n_lines(const char *ptr, int nl, int lenb) { int ml = 0; const char *pt2; if (nl <= 0) return ptr; while ((pt2 = memchr(ptr, '\n', lenb))) { ml++; if (ml == nl) break; lenb -= (pt2 + 1 - ptr); ptr = pt2 + 1; } return pt2 ? (pt2 + 1) : NULL; }