UNIXworkcode

/******************************************************************************* * * * refString.c -- Nirvana editor string handling * * * * Copyright (C) 200 Scott Tringali * * * * This is free software; you can redistribute it and/or modify it under the * * terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. In addition, you may distribute versions of this program linked to * * Motif or Open Motif. See README for details. * * * * This software is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * * for more details. * * * * You should have received a copy of the GNU General Public License along with * * software; if not, write to the Free Software Foundation, Inc., 59 Temple * * Place, Suite 330, Boston, MA 02111-1307 USA * * * * Nirvana Text Editor * * July, 1993 * * * * Written by Mark Edel * * * *******************************************************************************/ #include "refString.h" #include "nedit_malloc.h" #include <string.h> #include <stdio.h> #include <stdlib.h> #define RCS_SIZE 0x10000 struct rcs; struct rcs_stats { int talloc, tshar, tgiveup, tbytes, tbyteshared; }; struct rcs { struct rcs *next; char *string; int usage; }; static struct rcs *Rcs[RCS_SIZE]; static struct rcs_stats RcsStats; static unsigned const DJB2_SEED = 5381u; /* djb2s hash for null-terminated string, seeded version */ static unsigned djb2s(char const* key, unsigned hash) { char c; while (!!(c = *key++)) hash = ((hash << 5) + hash) ^ c; return hash; } /* Compute hash address from a string key */ unsigned StringHashAddr(const char *key) { return djb2s(key, DJB2_SEED); } /* Compute hash address from a null-termintated list of strings */ unsigned StringsHashAddr(const char** keys) { unsigned hash = DJB2_SEED; char const* key; while (!!(key = *keys++)) hash = djb2s(key, hash); return hash; } /* ** Take a normal string, create a shared string from it if need be, ** and return pointer to that shared string. ** ** Returned strings are const because they are shared. Do not modify them! */ const char *RefStringDup(const char *str) { unsigned bucket; size_t len; struct rcs *rp; char *newstr; if (str == NULL) return NULL; len = strlen(str); RcsStats.talloc++; /* Find it in hash */ bucket = StringHashAddr(str) % RCS_SIZE; rp = Rcs[bucket]; for (; rp; rp = rp->next) if (!strcmp(str, rp->string)) break; newstr = NULL; if (rp) /* It exists, return it and bump ref ct */ { rp->usage++; newstr = rp->string; RcsStats.tshar++; RcsStats.tbyteshared += len; } else /* Doesn't exist, conjure up a new one. */ { rp = NEditNew(struct rcs); rp->usage = 1; rp->next = Rcs[bucket]; Rcs[bucket] = rp; rp->string = (char*) NEditMalloc(len + 1); memcpy(rp->string, str, len + 1); newstr = rp->string; } RcsStats.tbytes += len; return newstr; } /* ** Decrease the reference count on a shared string. When the reference ** count reaches zero, free the master string. */ void RefStringFree(const char *rcs_str) { int bucket; struct rcs *rp; struct rcs *prev = NULL; if (rcs_str == NULL) return; bucket = StringHashAddr(rcs_str) % RCS_SIZE; /* find it in hash */ for (rp = Rcs[bucket]; rp; rp = rp->next) { if (rcs_str == rp->string) break; prev = rp; } if (rp) /* It's a shared string, decrease ref count */ { rp->usage--; if (rp->usage < 0) /* D'OH! */ { fprintf(stderr, "XNEdit: internal error deallocating shared string."); return; } if (rp->usage == 0) /* Last one- free the storage */ { NEditFree(rp->string); if (prev) prev->next = rp->next; else Rcs[bucket] = rp->next; NEditFree(rp); } } else /* Doesn't appear to be a shared string */ { fprintf(stderr, "XNEdit: attempt to free a non-shared string."); return; } }