|
|
1.1 root 1: /* js_queue.c */
2:
3: /* Synchronet JavaScript "Queue" Object */
4:
5: /* $Id: js_queue.c,v 1.9 2004/12/30 10:59:16 rswindell Exp $ */
6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
11: * Copyright 2004 Rob Swindell - http://www.synchro.net/copyright.html *
12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: #include "sbbs.h"
39: #include "msg_queue.h"
40:
41: typedef struct
42: {
43: char name[128];
44: int type;
45: union {
46: JSBool b;
47: jsdouble n;
48: char* s;
49: } value;
50: } queued_value_t;
51:
52: link_list_t named_queues;
53:
54: static const char* getprivate_failure = "line %d %s JS_GetPrivate failed";
55:
56: /* Queue Destructor */
57:
58: static void js_finalize_queue(JSContext *cx, JSObject *obj)
59: {
60: msg_queue_t* q;
61: list_node_t* n;
62:
63: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL)
64: return;
65:
66: if(msgQueueDetach(q)==0 && (n=listFindNode(&named_queues,q,/* length=0 for ptr compare */0))!=NULL)
67: listRemoveNode(&named_queues,n,TRUE);
68:
69: JS_SetPrivate(cx, obj, NULL);
70: }
71:
72: static size_t js_decode_value(JSContext *cx, JSObject *parent
73: ,queued_value_t* v, jsval* rval, BOOL peek)
74: {
75: size_t count=1;
76: size_t decoded;
77: queued_value_t* pv;
78: queued_value_t term;
79: jsval prop_val;
80: jsuint index=0;
81: JSObject *obj;
82:
83: ZERO_VAR(term);
84:
85: *rval = JSVAL_VOID;
86:
87: if(v==NULL || v->type==JSTYPE_VOID)
88: return(count);
89:
90: switch(v->type) {
91: case JSTYPE_BOOLEAN:
92: *rval = BOOLEAN_TO_JSVAL(v->value.b);
93: break;
94: case JSTYPE_NUMBER:
95: JS_NewNumberValue(cx,v->value.n,rval);
96: break;
97: case JSTYPE_STRING:
98: if(v->value.s) {
99: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,v->value.s));
100: if(!peek)
101: free(v->value.s);
102: }
103: break;
104: case JSTYPE_ARRAY:
105: case JSTYPE_OBJECT:
106: obj = JS_DefineObject(cx, parent, v->name, NULL, NULL
107: ,JSPROP_ENUMERATE);
108: for(pv=v+1,count++;memcmp(pv,&term,sizeof(term));pv+=decoded,count+=decoded) {
109: decoded=js_decode_value(cx,obj,pv,&prop_val,peek);
110: if(v->type==JSTYPE_ARRAY)
111: JS_SetElement(cx,obj,index++,&prop_val);
112: else
113: JS_DefineProperty(cx, obj, pv->name, prop_val,NULL,NULL,JSPROP_ENUMERATE);
114: }
115: *rval = OBJECT_TO_JSVAL(obj);
116: break;
117: }
118: return(count);
119: }
120:
121: /* Queue Object Methods */
122:
123: static JSBool
124: js_poll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
125: {
126: msg_queue_t* q;
127: queued_value_t* v;
128: int32 timeout=0;
129:
130: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
131: JS_ReportError(cx,getprivate_failure,WHERE);
132: return(JS_FALSE);
133: }
134:
135: if(argc) /* timeout specified */
136: JS_ValueToInt32(cx,argv[0],&timeout);
137:
138: if((v=msgQueuePeek(q,timeout))==NULL)
139: *rval = JSVAL_FALSE;
140: else if(v->name!=NULL && v->name[0])
141: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,v->name));
142: else
143: *rval = JSVAL_TRUE;
144:
145: return(JS_TRUE);
146: }
147:
148: static JSBool
149: js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
150: {
151: msg_queue_t* q;
152: queued_value_t find_v;
153: queued_value_t* v;
154:
155: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
156: JS_ReportError(cx,getprivate_failure,WHERE);
157: return(JS_FALSE);
158: }
159:
160: if(argc) { /* value named specified */
161: ZERO_VAR(find_v);
162: SAFECOPY(find_v.name,JS_GetStringBytes(JS_ValueToString(cx,argv[0])));
163: v=msgQueueFind(q,&find_v,sizeof(find_v.name));
164: } else
165: v=msgQueueRead(q, /* timeout */0);
166:
167: js_decode_value(cx, obj, v, rval, /* peek */FALSE);
168:
169: FREE_AND_NULL(v);
170:
171: return(JS_TRUE);
172: }
173:
174: static JSBool
175: js_peek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
176: {
177: msg_queue_t* q;
178: queued_value_t* v;
179:
180: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
181: JS_ReportError(cx,getprivate_failure,WHERE);
182: return(JS_FALSE);
183: }
184:
185: v=msgQueuePeek(q, /* timeout */0);
186:
187: js_decode_value(cx, obj, v, rval, /* peek */TRUE);
188:
189: FREE_AND_NULL(v);
190:
191: return(JS_TRUE);
192: }
193:
194: static queued_value_t* js_encode_value(JSContext *cx, jsval val, char* name
195: ,queued_value_t* v, size_t* count)
196: {
197: jsint i;
198: jsval prop_name;
199: jsval prop_val;
200: JSObject* obj;
201: JSIdArray* id_array;
202: queued_value_t* nv;
203:
204: if((nv=realloc(v,((*count)+1)*sizeof(queued_value_t)))==NULL) {
205: if(v) free(v);
206: return(NULL);
207: }
208: v=nv;
209: nv=v+(*count);
210: memset(nv,0,sizeof(queued_value_t));
211: (*count)++;
212:
213: if(name!=NULL)
214: SAFECOPY(nv->name,name);
215:
216: switch(JSVAL_TAG(val)) {
217: case JSVAL_BOOLEAN:
218: nv->type=JSTYPE_BOOLEAN;
219: nv->value.b=JSVAL_TO_BOOLEAN(val);
220: break;
221: case JSVAL_OBJECT:
222: nv->type=JSTYPE_OBJECT;
223: obj = JSVAL_TO_OBJECT(val);
224:
225: if(JSVAL_IS_NULL(val))
226: break;
227:
228: if(JS_IsArrayObject(cx, obj))
229: nv->type=JSTYPE_ARRAY;
230:
231: if((id_array=JS_Enumerate(cx,obj))==NULL) {
232: free(v);
233: return(NULL);
234: }
235: for(i=0; i<id_array->length; i++) {
236: /* property name */
237: JS_IdToValue(cx,id_array->vector[i],&prop_name);
238: if(JSVAL_IS_STRING(prop_name)) {
239: name=JS_GetStringBytes(JSVAL_TO_STRING(prop_name));
240: /* value */
241: JS_GetProperty(cx,obj,name,&prop_val);
242: } else {
243: name=NULL;
244: JS_GetElement(cx,obj,i,&prop_val);
245: }
246: if((v=js_encode_value(cx,prop_val,name,v,count))==NULL)
247: break;
248: }
249: v=js_encode_value(cx,JSVAL_VOID,NULL,v,count); /* terminate object */
250: break;
251: default:
252: if(JSVAL_IS_NUMBER(val)) {
253: nv->type = JSTYPE_NUMBER;
254: JS_ValueToNumber(cx,val,&nv->value.n);
255: } else if(JSVAL_IS_VOID(val)) {
256: nv->type = JSTYPE_VOID;
257: } else {
258: nv->type= JSTYPE_STRING;
259: nv->value.s = strdup(JS_GetStringBytes(JS_ValueToString(cx,val)));
260: }
261: break;
262: }
263:
264: return(v);
265: }
266:
267: BOOL js_enqueue_value(JSContext *cx, msg_queue_t* q, jsval val, char* name)
268: {
269: queued_value_t* v;
270: size_t count=0;
271: BOOL result;
272:
273: if((v=js_encode_value(cx,val,name,NULL,&count))==NULL || count<1)
274: return(FALSE);
275:
276: result=msgQueueWrite(q,v,count*sizeof(queued_value_t));
277: free(v);
278: return(result);
279: }
280:
281: static JSBool
282: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
283: {
284: uintN argn=0;
285: msg_queue_t* q;
286: jsval val;
287: char* name=NULL;
288:
289: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
290: JS_ReportError(cx,getprivate_failure,WHERE);
291: return(JS_FALSE);
292: }
293:
294: val = argv[argn++];
295:
296: if(argn < argc)
297: name=JS_GetStringBytes(JS_ValueToString(cx,argv[argn++]));
298:
299: *rval = BOOLEAN_TO_JSVAL(js_enqueue_value(cx, q, val, name));
300:
301: return(JS_TRUE);
302: }
303:
304: /* Queue Object Properites */
305: enum {
306: QUEUE_PROP_NAME
307: ,QUEUE_PROP_DATA_WAITING
308: ,QUEUE_PROP_READ_LEVEL
309: ,QUEUE_PROP_WRITE_LEVEL
310: };
311:
312: #ifdef _DEBUG
313: static char* queue_prop_desc[] = {
314: "name of the queue (if it has one)"
315: ,"<i>true</i> if data is waiting to be read from queue"
316: ,"number of values in the read queue"
317: ,"number of values in the write qeueue"
318: ,NULL
319: };
320: #endif
321:
322: static JSBool js_queue_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
323: {
324: jsint tiny;
325: msg_queue_t* q;
326:
327: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
328: JS_ReportError(cx,getprivate_failure,WHERE);
329: return(JS_FALSE);
330: }
331:
332: tiny = JSVAL_TO_INT(id);
333:
334: switch(tiny) {
335: case QUEUE_PROP_NAME:
336: if(q->name!=NULL && q->name[0])
337: *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,q->name));
338: break;
339: case QUEUE_PROP_DATA_WAITING:
340: *vp = BOOLEAN_TO_JSVAL(INT_TO_BOOL(msgQueueReadLevel(q)));
341: break;
342: case QUEUE_PROP_READ_LEVEL:
343: *vp = INT_TO_JSVAL(msgQueueReadLevel(q));
344: break;
345: case QUEUE_PROP_WRITE_LEVEL:
346: *vp = INT_TO_JSVAL(msgQueueWriteLevel(q));
347: break;
348: }
349: return(JS_TRUE);
350: }
351:
352: #define QUEUE_PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY
353:
354: static jsSyncPropertySpec js_queue_properties[] = {
355: /* name ,tinyid ,flags, ver */
356:
357: { "name" ,QUEUE_PROP_NAME ,QUEUE_PROP_FLAGS, 312 },
358: { "data_waiting" ,QUEUE_PROP_DATA_WAITING,QUEUE_PROP_FLAGS, 312 },
359: { "read_level" ,QUEUE_PROP_READ_LEVEL ,QUEUE_PROP_FLAGS, 312 },
360: { "write_level" ,QUEUE_PROP_WRITE_LEVEL ,QUEUE_PROP_FLAGS, 312 },
361: {0}
362: };
363:
364: static JSClass js_queue_class = {
365: "Queue" /* name */
366: ,JSCLASS_HAS_PRIVATE /* flags */
367: ,JS_PropertyStub /* addProperty */
368: ,JS_PropertyStub /* delProperty */
369: ,js_queue_get /* getProperty */
370: ,JS_PropertyStub /* setProperty */
371: ,JS_EnumerateStub /* enumerate */
372: ,JS_ResolveStub /* resolve */
373: ,JS_ConvertStub /* convert */
374: ,js_finalize_queue /* finalize */
375: };
376:
377: static jsSyncMethodSpec js_queue_functions[] = {
378: {"poll", js_poll, 0, JSTYPE_UNDEF, "[timeout]"
379: ,JSDOCSTR("wait for a value on the queue for up to <i>timeout</i> milliseconds "
380: "(default: <i>infinite</i>), returns <i>true</i> or the <i>name</i> (string) of "
381: "the value waiting (if it has one), or <i>false</i> if no values are waiting")
382: ,312
383: },
384: {"read", js_read, 0, JSTYPE_UNDEF, "[name]"
385: ,JSDOCSTR("read a value from the queue, if <i>name</i> not specified, reads next value "
386: "from the bottom of the queue")
387: ,312
388: },
389: {"peek", js_peek, 0, JSTYPE_UNDEF, ""
390: ,JSDOCSTR("peek at the value at the bottom of the queue")
391: ,312
392: },
393: {"write", js_write, 0, JSTYPE_BOOLEAN, "value [,name]"
394: ,JSDOCSTR("write a value (optionally named) to the queue")
395: ,312
396: },
397: {0}
398: };
399:
400: /* Queue Constructor (creates queue) */
401:
402: static JSBool
403: js_queue_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
404: {
405: uintN argn=0;
406: char* name=NULL;
407: int32 flags=MSG_QUEUE_BIDIR;
408: msg_queue_t* q=NULL;
409: list_node_t* n;
410:
411: *rval = JSVAL_VOID;
412:
413: if((q=(msg_queue_t*)malloc(sizeof(msg_queue_t)))==NULL) {
414: JS_ReportError(cx,"malloc failed");
415: return(JS_FALSE);
416: }
417: memset(q,0,sizeof(msg_queue_t));
418:
419: if(argn<argc && JSVAL_IS_STRING(argv[argn]))
420: name=JS_GetStringBytes(JS_ValueToString(cx,argv[argn++]));
421:
422: if(argn<argc && JSVAL_IS_NUMBER(argv[argn]))
423: JS_ValueToInt32(cx,argv[argn++],&flags);
424:
425: if(name!=NULL) {
426: for(n=listFirstNode(&named_queues);n!=NULL;n=listNextNode(n))
427: if((q=n->data)!=NULL && !stricmp(q->name,name))
428: break;
429: if(n==NULL)
430: q=NULL;
431: }
432:
433: if(q==NULL) {
434: q=msgQueueInit(NULL,flags);
435: if(name!=NULL)
436: SAFECOPY(q->name,name);
437: listPushNode(&named_queues,q);
438: } else
439: msgQueueAttach(q);
440:
441: if(!JS_SetPrivate(cx, obj, q)) {
442: JS_ReportError(cx,"JS_SetPrivate failed");
443: return(JS_FALSE);
444: }
445:
446: if(!js_DefineSyncProperties(cx, obj, js_queue_properties)) {
447: JS_ReportError(cx,"js_DefineSyncProperties failed");
448: return(JS_FALSE);
449: }
450:
451: if(!js_DefineSyncMethods(cx, obj, js_queue_functions, FALSE)) {
452: JS_ReportError(cx,"js_DefineSyncMethods failed");
453: return(JS_FALSE);
454: }
455:
456: #ifdef _DEBUG
457: js_DescribeSyncObject(cx,obj,"Class for bi-directional message queues. "
458: "Used for inter-thread/module communications.", 312);
459: js_DescribeSyncConstructor(cx,obj,"To create a new (named) Queue object: "
460: "<tt>var q = new Queue(<i>name</i>)</tt>");
461: js_CreateArrayOfStrings(cx, obj, "_property_desc_list", queue_prop_desc, JSPROP_READONLY);
462: #endif
463:
464: return(JS_TRUE);
465: }
466:
467: JSObject* DLLCALL js_CreateQueueClass(JSContext* cx, JSObject* parent)
468: {
469: JSObject* obj;
470:
471: obj = JS_InitClass(cx, parent, NULL
472: ,&js_queue_class
473: ,js_queue_constructor
474: ,0 /* number of constructor args */
475: ,NULL /* props, specified in constructor */
476: ,NULL /* funcs, specified in constructor */
477: ,NULL
478: ,NULL);
479:
480: return(obj);
481: }
482:
483: JSObject* DLLCALL js_CreateQueueObject(JSContext* cx, JSObject* parent, char *name, msg_queue_t* q)
484: {
485: JSObject* obj;
486:
487: if(name==NULL)
488: obj = JS_NewObject(cx, &js_queue_class, NULL, parent);
489: else
490: obj = JS_DefineObject(cx, parent, name, &js_queue_class, NULL
491: ,JSPROP_ENUMERATE|JSPROP_READONLY);
492:
493: if(obj==NULL)
494: return(NULL);
495:
496: if(!js_DefineSyncProperties(cx, obj, js_queue_properties))
497: return(NULL);
498:
499: if(!JS_SetPrivate(cx, obj, q))
500: return(NULL);
501:
502: if (!js_DefineSyncMethods(cx, obj, js_queue_functions, FALSE))
503: return(NULL);
504:
505: return(obj);
506: }
507:
508:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.