Annotation of XML/xmlmemory.c, revision 1.18
1.1 daniel 1: /*
2: * memory.c: libxml memory allocator wrapper.
3: *
4: * Daniel.Veillard@w3.org
5: */
6:
1.3 daniel 7: #ifdef WIN32
1.9 daniel 8: #include "win32config.h"
1.3 daniel 9: #else
1.4 daniel 10: #include "config.h"
1.3 daniel 11: #endif
12:
13: #include <stdio.h>
14: #include <string.h>
15:
16: #ifdef HAVE_SYS_TYPES_H
1.1 daniel 17: #include <sys/types.h>
1.3 daniel 18: #endif
1.4 daniel 19: #ifdef HAVE_TIME_H
20: #include <time.h>
21: #endif
1.3 daniel 22: #ifdef HAVE_MALLOC_H
1.1 daniel 23: #include <malloc.h>
1.3 daniel 24: #endif
1.7 daniel 25: #ifdef HAVE_STDLIB_H
26: #include <stdlib.h>
27: #endif
1.11 daniel 28: #ifdef HAVE_CTYPE_H
29: #include <ctype.h>
30: #endif
1.7 daniel 31:
1.3 daniel 32:
1.14 daniel 33: #include <libxml/xmlmemory.h>
1.1 daniel 34:
35: #ifdef xmlMalloc
36: #undef xmlMalloc
37: #endif
38: #ifdef xmlRealloc
39: #undef xmlRealloc
40: #endif
41: #ifdef xmlMemStrdup
42: #undef xmlMemStrdup
43: #endif
1.6 daniel 44:
1.1 daniel 45:
46: /*
47: * Each of the blocks allocated begin with a header containing informations
48: */
49:
50: #define MEMTAG 0x5aa5
51:
52: #define MALLOC_TYPE 1
53: #define REALLOC_TYPE 2
54: #define STRDUP_TYPE 3
55:
56: typedef struct memnod {
57: unsigned int mh_tag;
58: unsigned int mh_type;
59: unsigned long mh_number;
60: size_t mh_size;
61: #ifdef MEM_LIST
62: struct memnod *mh_next;
63: struct memnod *mh_prev;
64: #endif
65: const char *mh_file;
66: unsigned int mh_line;
67: } MEMHDR;
68:
69:
70: #ifdef SUN4
71: #define ALIGN_SIZE 16
72: #else
73: #define ALIGN_SIZE sizeof(double)
74: #endif
75: #define HDR_SIZE sizeof(MEMHDR)
76: #define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \
77: / ALIGN_SIZE ) * ALIGN_SIZE)
78:
79:
80: #define CLIENT_2_HDR(a) ((MEMHDR *) (((char *) (a)) - RESERVE_SIZE))
81: #define HDR_2_CLIENT(a) ((void *) (((char *) (a)) + RESERVE_SIZE))
82:
83:
84: static unsigned long debugMemSize = 0;
1.7 daniel 85: static unsigned long debugMaxMemSize = 0;
1.1 daniel 86: static int block=0;
1.7 daniel 87: int xmlMemStopAtBlock = 0;
88: int xmlMemInitialized = 0;
1.1 daniel 89: #ifdef MEM_LIST
90: static MEMHDR *memlist = NULL;
91: #endif
92:
93: void debugmem_tag_error(void *addr);
94: #ifdef MEM_LIST
95: void debugmem_list_add(MEMHDR *);
96: void debugmem_list_delete(MEMHDR *);
97: #endif
98: #define Mem_Tag_Err(a) debugmem_tag_error(a);
99:
100: #ifndef TEST_POINT
101: #define TEST_POINT
102: #endif
103:
104: /**
1.7 daniel 105: * xmlMallocBreakpoint:
106: *
107: * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block
108: * number reaches the specified value this function is called. One need to add a breakpoint
109: * to it to get the context in which the given block is allocated.
110: */
111:
112: void
113: xmlMallocBreakpoint(void) {
114: fprintf(stderr, "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock);
115: }
116:
117: /**
1.1 daniel 118: * xmlMallocLoc:
119: * @size: an int specifying the size in byte to allocate.
120: * @file: the file name or NULL
1.15 daniel 121: * @line: the line number
1.1 daniel 122: *
123: * a malloc() equivalent, with logging of the allocation info.
124: *
125: * Returns a pointer to the allocated area or NULL in case of lack of memory.
126: */
127:
128: void *
129: xmlMallocLoc(int size, const char * file, int line)
130: {
131: MEMHDR *p;
132:
1.7 daniel 133: if (!xmlMemInitialized) xmlInitMemory();
1.1 daniel 134: #ifdef DEBUG_MEMORY
135: fprintf(stderr, "Malloc(%d)\n",size);
136: #endif
137:
138: TEST_POINT
139:
140: p = (MEMHDR *) malloc(RESERVE_SIZE+size);
141:
142: if (!p) {
1.8 daniel 143: fprintf(stderr, "xmlMalloc : Out of free space\n");
144: xmlMemoryDump();
145: return(NULL);
1.1 daniel 146: }
147: p->mh_tag = MEMTAG;
148: p->mh_number = ++block;
149: p->mh_size = size;
150: p->mh_type = MALLOC_TYPE;
151: p->mh_file = file;
152: p->mh_line = line;
153: debugMemSize += size;
1.7 daniel 154: if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
1.1 daniel 155: #ifdef MEM_LIST
156: debugmem_list_add(p);
157: #endif
158:
159: #ifdef DEBUG_MEMORY
160: fprintf(stderr, "Malloc(%d) Ok\n",size);
161: #endif
162:
1.7 daniel 163: if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
1.1 daniel 164:
165: TEST_POINT
166:
167: return(HDR_2_CLIENT(p));
168: }
169:
170: /**
1.12 daniel 171: * xmlMemMalloc:
1.1 daniel 172: * @size: an int specifying the size in byte to allocate.
173: *
174: * a malloc() equivalent, with logging of the allocation info.
175: *
176: * Returns a pointer to the allocated area or NULL in case of lack of memory.
177: */
178:
179: void *
1.12 daniel 180: xmlMemMalloc(int size)
1.1 daniel 181: {
182: return(xmlMallocLoc(size, "none", 0));
183: }
184:
185: /**
186: * xmlReallocLoc:
187: * @ptr: the initial memory block pointer
188: * @size: an int specifying the size in byte to allocate.
189: * @file: the file name or NULL
1.15 daniel 190: * @line: the line number
1.1 daniel 191: *
192: * a realloc() equivalent, with logging of the allocation info.
193: *
194: * Returns a pointer to the allocated area or NULL in case of lack of memory.
195: */
196:
197: void *
198: xmlReallocLoc(void *ptr,int size, const char * file, int line)
199: {
200: MEMHDR *p;
201: unsigned long number;
202:
1.7 daniel 203: if (!xmlMemInitialized) xmlInitMemory();
1.1 daniel 204: TEST_POINT
205:
206: p = CLIENT_2_HDR(ptr);
207: number = p->mh_number;
208: if (p->mh_tag != MEMTAG) {
1.4 daniel 209: Mem_Tag_Err(p);
1.1 daniel 210: goto error;
211: }
212: p->mh_tag = ~MEMTAG;
213: debugMemSize -= p->mh_size;
214: #ifdef MEM_LIST
215: debugmem_list_delete(p);
216: #endif
217:
218: p = (MEMHDR *) realloc(p,RESERVE_SIZE+size);
219: if (!p) {
220: goto error;
221: }
222: p->mh_tag = MEMTAG;
223: p->mh_number = number;
224: p->mh_type = REALLOC_TYPE;
225: p->mh_size = size;
226: p->mh_file = file;
227: p->mh_line = line;
228: debugMemSize += size;
1.7 daniel 229: if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
1.1 daniel 230: #ifdef MEM_LIST
231: debugmem_list_add(p);
232: #endif
233:
234: TEST_POINT
235:
236: return(HDR_2_CLIENT(p));
237:
238: error:
239: return(NULL);
240: }
241:
242: /**
1.12 daniel 243: * xmlMemRealloc:
1.1 daniel 244: * @ptr: the initial memory block pointer
245: * @size: an int specifying the size in byte to allocate.
246: *
247: * a realloc() equivalent, with logging of the allocation info.
248: *
249: * Returns a pointer to the allocated area or NULL in case of lack of memory.
250: */
251:
252: void *
1.12 daniel 253: xmlMemRealloc(void *ptr,int size) {
1.1 daniel 254: return(xmlReallocLoc(ptr, size, "none", 0));
255: }
256:
257: /**
1.12 daniel 258: * xmlMemFree:
1.1 daniel 259: * @ptr: the memory block pointer
260: *
261: * a free() equivalent, with error checking.
262: */
263: void
1.12 daniel 264: xmlMemFree(void *ptr)
1.1 daniel 265: {
266: MEMHDR *p;
267:
268: TEST_POINT
269:
270: p = CLIENT_2_HDR(ptr);
271: if (p->mh_tag != MEMTAG) {
1.4 daniel 272: Mem_Tag_Err(p);
273: goto error;
1.1 daniel 274: }
275: p->mh_tag = ~MEMTAG;
276: debugMemSize -= p->mh_size;
277:
278: #ifdef MEM_LIST
279: debugmem_list_delete(p);
280: #endif
281: free(p);
282:
283: TEST_POINT
284:
285: return;
286:
287: error:
288: fprintf(stderr, "xmlFree(%X) error\n", (unsigned int) ptr);
289: return;
290: }
291:
292: /**
293: * xmlMemStrdupLoc:
294: * @ptr: the initial string pointer
295: * @file: the file name or NULL
1.15 daniel 296: * @line: the line number
1.1 daniel 297: *
298: * a strdup() equivalent, with logging of the allocation info.
299: *
300: * Returns a pointer to the new string or NULL if allocation error occured.
301: */
302:
303: char *
304: xmlMemStrdupLoc(const char *str, const char *file, int line)
305: {
306: char *s;
307: size_t size = strlen(str) + 1;
308: MEMHDR *p;
309:
1.7 daniel 310: if (!xmlMemInitialized) xmlInitMemory();
1.1 daniel 311: TEST_POINT
312:
313: p = (MEMHDR *) malloc(RESERVE_SIZE+size);
314: if (!p) {
1.4 daniel 315: goto error;
1.1 daniel 316: }
317: p->mh_tag = MEMTAG;
318: p->mh_number = ++block;
319: p->mh_size = size;
320: p->mh_type = STRDUP_TYPE;
321: p->mh_file = file;
322: p->mh_line = line;
323: debugMemSize += size;
1.7 daniel 324: if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
1.1 daniel 325: #ifdef MEM_LIST
326: debugmem_list_add(p);
327: #endif
1.16 veillard 328: s = (char *) HDR_2_CLIENT(p);
1.1 daniel 329:
1.7 daniel 330: if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
331:
1.1 daniel 332: if (s != NULL)
1.4 daniel 333: strcpy(s,str);
1.1 daniel 334: else
1.4 daniel 335: goto error;
1.1 daniel 336:
337: TEST_POINT
338:
339: return(s);
340:
341: error:
342: return(NULL);
343: }
344:
345: /**
1.12 daniel 346: * xmlMemoryStrdup:
1.1 daniel 347: * @ptr: the initial string pointer
348: *
349: * a strdup() equivalent, with logging of the allocation info.
350: *
351: * Returns a pointer to the new string or NULL if allocation error occured.
352: */
353:
354: char *
1.12 daniel 355: xmlMemoryStrdup(const char *str) {
1.1 daniel 356: return(xmlMemStrdupLoc(str, "none", 0));
357: }
358:
359: /**
360: * xmlMemUsed:
361: *
362: * returns the amount of memory currenly allocated
363: *
364: * Returns an int representing the amount of memory allocated.
365: */
366:
367: int
368: xmlMemUsed(void) {
369: return(debugMemSize);
370: }
371:
1.11 daniel 372: #ifdef MEM_LIST
373: /**
374: * xmlMemContentShow:
375: * @fp: a FILE descriptor used as the output file
376: * @p: a memory block header
377: *
378: * tries to show some content from the memory block
379: */
380:
381: void
382: xmlMemContentShow(FILE *fp, MEMHDR *p)
383: {
384: int i,j,len = p->mh_size;
1.16 veillard 385: const char *buf = (const char *) HDR_2_CLIENT(p);
1.11 daniel 386:
1.18 ! veillard 387: if (p == NULL) {
! 388: fprintf(fp, " NULL");
! 389: return;
! 390: }
! 391:
1.11 daniel 392: for (i = 0;i < len;i++) {
393: if (buf[i] == 0) break;
394: if (!isprint(buf[i])) break;
395: }
396: if ((i < 4) && ((buf[i] != 0) || (i == 0))) {
397: if (len >= 4) {
398: MEMHDR *q;
399: void *cur;
400:
401: for (j = 0;j < len -3;j += 4) {
402: cur = *((void **) &buf[j]);
403: q = CLIENT_2_HDR(cur);
404: p = memlist;
405: while (p != NULL) {
406: if (p == q) break;
407: p = p->mh_next;
408: }
1.18 ! veillard 409: if ((p != NULL) && (p == q)) {
1.11 daniel 410: fprintf(fp, " pointer to #%lu at index %d",
411: p->mh_number, j);
412: return;
413: }
414: }
415: }
416: } else if ((i == 0) && (buf[i] == 0)) {
417: fprintf(fp," null");
418: } else {
419: if (buf[i] == 0) fprintf(fp," \"%.25s\"", buf);
420: else {
421: fprintf(fp," [");
422: for (j = 0;j < i;j++)
423: fprintf(fp,"%c", buf[j]);
424: fprintf(fp,"]");
425: }
426: }
427: }
428: #endif
429:
1.1 daniel 430: /**
1.10 daniel 431: * xmlMemShow:
432: * @fp: a FILE descriptor used as the output file
433: * @nr: number of entries to dump
434: *
435: * show a show display of the memory allocated, and dump
436: * the @nr last allocated areas which were not freed
437: */
438:
439: void
440: xmlMemShow(FILE *fp, int nr)
441: {
442: #ifdef MEM_LIST
443: MEMHDR *p;
444: #endif
445:
446: if (fp != NULL)
447: fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n",
448: debugMemSize, debugMaxMemSize);
449: #ifdef MEM_LIST
450: if (nr > 0) {
451: fprintf(fp,"NUMBER SIZE TYPE WHERE\n");
452: p = memlist;
453: while ((p) && nr > 0) {
454: fprintf(fp,"%6lu %6u ",p->mh_number,p->mh_size);
455: switch (p->mh_type) {
456: case STRDUP_TYPE:fprintf(fp,"strdup() in ");break;
457: case MALLOC_TYPE:fprintf(fp,"malloc() in ");break;
458: case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
459: default:fprintf(fp," ??? in ");break;
460: }
461: if (p->mh_file != NULL)
462: fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
463: if (p->mh_tag != MEMTAG)
464: fprintf(fp," INVALID");
1.11 daniel 465: xmlMemContentShow(fp, p);
1.10 daniel 466: fprintf(fp,"\n");
467: nr--;
468: p = p->mh_next;
469: }
470: }
471: #endif /* MEM_LIST */
472: }
473:
474: /**
1.1 daniel 475: * xmlMemDisplay:
476: * @fp: a FILE descriptor used as the output file, if NULL, the result is
1.10 daniel 477: * written to the file .memorylist
1.1 daniel 478: *
479: * show in-extenso the memory blocks allocated
480: */
481:
482: void
483: xmlMemDisplay(FILE *fp)
484: {
485: #ifdef MEM_LIST
1.4 daniel 486: MEMHDR *p;
487: int idx;
488: #if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
1.5 daniel 489: time_t currentTime;
1.4 daniel 490: char buf[500];
491: struct tm * tstruct;
492:
1.5 daniel 493: currentTime = time(NULL);
494: tstruct = localtime(¤tTime);
1.4 daniel 495: strftime(buf, sizeof(buf) - 1, "%c", tstruct);
496: fprintf(fp," %s\n\n", buf);
497: #endif
498:
1.1 daniel 499:
1.7 daniel 500: fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n",
501: debugMemSize, debugMaxMemSize);
1.4 daniel 502: fprintf(fp,"BLOCK NUMBER SIZE TYPE\n");
503: idx = 0;
504: p = memlist;
505: while (p) {
1.1 daniel 506: fprintf(fp,"%-5u %6lu %6u ",idx++,p->mh_number,p->mh_size);
1.4 daniel 507: switch (p->mh_type) {
508: case STRDUP_TYPE:fprintf(fp,"strdup() in ");break;
509: case MALLOC_TYPE:fprintf(fp,"malloc() in ");break;
510: case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
511: default:fprintf(fp," ??? in ");break;
512: }
1.1 daniel 513: if (p->mh_file != NULL) fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
1.4 daniel 514: if (p->mh_tag != MEMTAG)
1.1 daniel 515: fprintf(fp," INVALID");
1.11 daniel 516: xmlMemContentShow(fp, p);
1.4 daniel 517: fprintf(fp,"\n");
518: p = p->mh_next;
519: }
1.1 daniel 520: #else
1.4 daniel 521: fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
1.1 daniel 522: #endif
523: }
524:
525: #ifdef MEM_LIST
526:
527: void debugmem_list_add(MEMHDR *p)
528: {
1.4 daniel 529: p->mh_next = memlist;
530: p->mh_prev = NULL;
531: if (memlist) memlist->mh_prev = p;
532: memlist = p;
1.1 daniel 533: #ifdef MEM_LIST_DEBUG
1.4 daniel 534: if (stderr)
535: Mem_Display(stderr);
1.1 daniel 536: #endif
537: }
538:
539: void debugmem_list_delete(MEMHDR *p)
540: {
1.4 daniel 541: if (p->mh_next)
542: p->mh_next->mh_prev = p->mh_prev;
543: if (p->mh_prev)
544: p->mh_prev->mh_next = p->mh_next;
545: else memlist = p->mh_next;
1.1 daniel 546: #ifdef MEM_LIST_DEBUG
1.4 daniel 547: if (stderr)
548: Mem_Display(stderr);
1.1 daniel 549: #endif
550: }
551:
552: #endif
553:
554: /*
555: * debugmem_tag_error : internal error function.
556: */
557:
558: void debugmem_tag_error(void *p)
559: {
1.4 daniel 560: fprintf(stderr, "Memory tag error occurs :%p \n\t bye\n", p);
1.1 daniel 561: #ifdef MEM_LIST
1.4 daniel 562: if (stderr)
563: xmlMemDisplay(stderr);
1.1 daniel 564: #endif
565: }
566:
567: FILE *xmlMemoryDumpFile = NULL;
568:
569:
570: /**
571: * xmlMemoryDump:
572: *
573: * Dump in-extenso the memory blocks allocated to the file .memorylist
574: */
575:
576: void
577: xmlMemoryDump(void)
578: {
1.13 daniel 579: #if defined(DEBUG_MEMORY_LOCATION) | defined(DEBUG_MEMORY)
1.1 daniel 580: FILE *dump;
581:
582: dump = fopen(".memdump", "w");
583: if (dump == NULL) xmlMemoryDumpFile = stdout;
584: else xmlMemoryDumpFile = dump;
585:
586: xmlMemDisplay(xmlMemoryDumpFile);
587:
588: if (dump != NULL) fclose(dump);
1.13 daniel 589: #endif
1.1 daniel 590: }
591:
592:
593: /****************************************************************
594: * *
595: * Initialization Routines *
596: * *
597: ****************************************************************/
598:
1.12 daniel 599: #if defined(DEBUG_MEMORY_LOCATION) | defined(DEBUG_MEMORY)
1.13 daniel 600: xmlFreeFunc xmlFree = (xmlFreeFunc) xmlMemFree;
601: xmlMallocFunc xmlMalloc = (xmlMallocFunc) xmlMemMalloc;
602: xmlReallocFunc xmlRealloc = (xmlReallocFunc) xmlMemRealloc;
603: xmlStrdupFunc xmlMemStrdup = (xmlStrdupFunc) xmlMemoryStrdup;
1.12 daniel 604: #else
1.13 daniel 605: xmlFreeFunc xmlFree = (xmlFreeFunc) free;
606: xmlMallocFunc xmlMalloc = (xmlMallocFunc) malloc;
607: xmlReallocFunc xmlRealloc = (xmlReallocFunc) realloc;
608: xmlStrdupFunc xmlMemStrdup = (xmlStrdupFunc) strdup;
1.12 daniel 609: #endif
610:
1.1 daniel 611: /**
612: * xmlInitMemory:
613: *
614: * Initialize the memory layer.
1.6 daniel 615: *
616: * Returns 0 on success
1.1 daniel 617: */
618:
1.12 daniel 619: static int xmlInitMemoryDone = 0;
1.1 daniel 620:
621: int
622: xmlInitMemory(void)
623: {
624: int ret;
1.7 daniel 625:
626: #ifdef HAVE_STDLIB_H
627: char *breakpoint;
1.12 daniel 628: #endif
629:
630: if (xmlInitMemoryDone) return(-1);
1.7 daniel 631:
1.12 daniel 632: #ifdef HAVE_STDLIB_H
1.7 daniel 633: breakpoint = getenv("XML_MEM_BREAKPOINT");
634: if (breakpoint != NULL) {
635: sscanf(breakpoint, "%d", &xmlMemStopAtBlock);
636: }
637: #endif
1.1 daniel 638:
639: #ifdef DEBUG_MEMORY
640: fprintf(stderr, "xmlInitMemory() Ok\n");
641: #endif
642: ret = 0;
643: return(ret);
1.12 daniel 644: }
645:
646: /**
647: * xmlMemSetup:
648: * @freeFunc: the free() function to use
649: * @mallocFunc: the malloc() function to use
650: * @reallocFunc: the realloc() function to use
651: * @strdupFunc: the strdup() function to use
652: *
653: * Override the default memory access functions with a new set
1.13 daniel 654: * This has to be called before any other libxml routines !
655: *
656: * Should this be blocked if there was already some allocations
657: * done ?
1.12 daniel 658: *
659: * Returns 0 on success
660: */
661: int
662: xmlMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc,
663: xmlReallocFunc reallocFunc, xmlStrdupFunc strdupFunc) {
1.17 veillard 664: if (freeFunc == NULL)
1.12 daniel 665: return(-1);
1.17 veillard 666: if (mallocFunc == NULL)
1.12 daniel 667: return(-1);
1.17 veillard 668: if (reallocFunc == NULL)
1.12 daniel 669: return(-1);
1.17 veillard 670: if (strdupFunc == NULL)
1.12 daniel 671: return(-1);
672: xmlFree = freeFunc;
673: xmlMalloc = mallocFunc;
674: xmlRealloc = reallocFunc;
675: xmlMemStrdup = strdupFunc;
676: return(0);
677: }
678:
679: /**
680: * xmlMemGet:
681: * @freeFunc: the free() function in use
682: * @mallocFunc: the malloc() function in use
683: * @reallocFunc: the realloc() function in use
684: * @strdupFunc: the strdup() function in use
685: *
686: * Return the memory access functions set currently in use
687: *
688: * Returns 0 on success
689: */
690: int
691: xmlMemGet(xmlFreeFunc *freeFunc, xmlMallocFunc *mallocFunc,
692: xmlReallocFunc *reallocFunc, xmlStrdupFunc *strdupFunc) {
1.13 daniel 693: if (freeFunc != NULL) *freeFunc = xmlFree;
694: if (mallocFunc != NULL) *mallocFunc = xmlMalloc;
695: if (reallocFunc != NULL) *reallocFunc = xmlRealloc;
696: if (strdupFunc != NULL) *strdupFunc = xmlMemStrdup;
1.12 daniel 697: return(0);
1.1 daniel 698: }
699:
Webmaster