|
|
1.1 root 1: /* js_queue.c */
2:
3: /* Synchronet JavaScript "Queue" Object */
4:
5: /* $Id: js_queue.c,v 1.18 2006/06/28 22:50:27 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 2006 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,FALSE);
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_NULL:
92: *rval = JSVAL_NULL;
93: break;
94: case JSTYPE_BOOLEAN:
95: *rval = BOOLEAN_TO_JSVAL(v->value.b);
96: break;
97: case JSTYPE_NUMBER:
98: JS_NewNumberValue(cx,v->value.n,rval);
99: break;
100: case JSTYPE_STRING:
101: if(v->value.s) {
102: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,v->value.s));
103: if(!peek)
104: free(v->value.s);
105: }
106: break;
107: case JSTYPE_ARRAY:
108: case JSTYPE_OBJECT:
109: obj = JS_DefineObject(cx, parent, v->name, NULL, NULL
110: ,JSPROP_ENUMERATE);
111: for(pv=v+1,count++;memcmp(pv,&term,sizeof(term));pv+=decoded,count+=decoded) {
112: decoded=js_decode_value(cx,obj,pv,&prop_val,peek);
113: if(v->type==JSTYPE_ARRAY)
114: JS_SetElement(cx,obj,index++,&prop_val);
115: else
116: JS_DefineProperty(cx, obj, pv->name, prop_val,NULL,NULL,JSPROP_ENUMERATE);
117: }
118: *rval = OBJECT_TO_JSVAL(obj);
119: break;
120: }
121: return(count);
122: }
123:
124: /* Queue Object Methods */
125:
126: static JSBool
127: js_poll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
128: {
129: msg_queue_t* q;
130: queued_value_t* v;
131: int32 timeout=0;
132:
133: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
134: JS_ReportError(cx,getprivate_failure,WHERE);
135: return(JS_FALSE);
136: }
137:
138: if(argc && JSVAL_IS_NUMBER(argv[0])) /* timeout specified */
139: JS_ValueToInt32(cx,argv[0],&timeout);
140:
141: if((v=msgQueuePeek(q,timeout))==NULL)
142: *rval = JSVAL_FALSE;
143: else if(v->name!=NULL && v->name[0])
144: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,v->name));
145: else
146: *rval = JSVAL_TRUE;
147:
148: return(JS_TRUE);
149: }
150:
151: static JSBool
152: js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
153: {
154: msg_queue_t* q;
155: queued_value_t find_v;
156: queued_value_t* v;
157: int32 timeout=0;
158:
159: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
160: JS_ReportError(cx,getprivate_failure,WHERE);
161: return(JS_FALSE);
162: }
163:
164: if(JSVAL_IS_STRING(argv[0])) { /* value named specified */
165: ZERO_VAR(find_v);
166: SAFECOPY(find_v.name,JS_GetStringBytes(JS_ValueToString(cx,argv[0])));
167: v=msgQueueFind(q,&find_v,sizeof(find_v.name));
168: } else {
169: if(JSVAL_IS_NUMBER(argv[0]))
170: JS_ValueToInt32(cx,argv[0],&timeout);
171: v=msgQueueRead(q, timeout);
172: }
173:
174: if(v!=NULL) {
175: js_decode_value(cx, obj, v, rval, /* peek */FALSE);
176: free(v);
177: }
178:
179: return(JS_TRUE);
180: }
181:
182: static JSBool
183: js_peek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
184: {
185: msg_queue_t* q;
186: queued_value_t* v;
187: int32 timeout=0;
188:
189: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
190: JS_ReportError(cx,getprivate_failure,WHERE);
191: return(JS_FALSE);
192: }
193:
194: if(argc && JSVAL_IS_NUMBER(argv[0])) /* timeout specified */
195: JS_ValueToInt32(cx,argv[0],&timeout);
196:
197: if((v=msgQueuePeek(q, timeout))!=NULL) {
198: js_decode_value(cx, obj, v, rval, /* peek */TRUE);
199: }
200:
201: return(JS_TRUE);
202: }
203:
204: static queued_value_t* js_encode_value(JSContext *cx, jsval val, char* name
205: ,queued_value_t* v, size_t* count)
206: {
207: jsint i;
208: jsval prop_name;
209: jsval prop_val;
210: JSObject* obj;
211: JSIdArray* id_array;
212: queued_value_t* nv;
213:
214: if((nv=realloc(v,((*count)+1)*sizeof(queued_value_t)))==NULL) {
215: if(v) free(v);
216: return(NULL);
217: }
218: v=nv;
219: nv=v+(*count);
220: memset(nv,0,sizeof(queued_value_t));
221: (*count)++;
222:
223: if(name!=NULL)
224: SAFECOPY(nv->name,name);
225:
226: switch(JSVAL_TAG(val)) {
227: case JSVAL_BOOLEAN:
228: nv->type=JSTYPE_BOOLEAN;
229: nv->value.b=JSVAL_TO_BOOLEAN(val);
230: break;
231: case JSVAL_OBJECT:
232: if(JSVAL_IS_NULL(val)) {
233: nv->type=JSTYPE_NULL;
234: break;
235: }
236: nv->type=JSTYPE_OBJECT;
237: obj = JSVAL_TO_OBJECT(val);
238:
239: if(JS_IsArrayObject(cx, obj))
240: nv->type=JSTYPE_ARRAY;
241:
242: if((id_array=JS_Enumerate(cx,obj))==NULL) {
243: free(v);
244: return(NULL);
245: }
246: for(i=0; i<id_array->length; i++) {
247: /* property name */
248: JS_IdToValue(cx,id_array->vector[i],&prop_name);
249: if(JSVAL_IS_STRING(prop_name)) {
250: name=JS_GetStringBytes(JSVAL_TO_STRING(prop_name));
251: /* value */
252: JS_GetProperty(cx,obj,name,&prop_val);
253: } else {
254: name=NULL;
255: JS_GetElement(cx,obj,i,&prop_val);
256: }
257: if((v=js_encode_value(cx,prop_val,name,v,count))==NULL)
258: break;
259: }
260: v=js_encode_value(cx,JSVAL_VOID,NULL,v,count); /* terminate object */
261: JS_DestroyIdArray(cx,id_array);
262: break;
263: default:
264: if(JSVAL_IS_NUMBER(val)) {
265: nv->type = JSTYPE_NUMBER;
266: JS_ValueToNumber(cx,val,&nv->value.n);
267: } else if(JSVAL_IS_VOID(val)) {
268: nv->type = JSTYPE_VOID;
269: } else {
270: nv->type= JSTYPE_STRING;
271: nv->value.s = strdup(JS_GetStringBytes(JS_ValueToString(cx,val)));
272: }
273: break;
274: }
275:
276: return(v);
277: }
278:
279: BOOL js_enqueue_value(JSContext *cx, msg_queue_t* q, jsval val, char* name)
280: {
281: queued_value_t* v;
282: size_t count=0;
283: BOOL result;
284:
285: if((v=js_encode_value(cx,val,name,NULL,&count))==NULL || count<1)
286: return(FALSE);
287:
288: result=msgQueueWrite(q,v,count*sizeof(queued_value_t));
289: free(v);
290: return(result);
291: }
292:
293: static JSBool
294: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
295: {
296: uintN argn=0;
297: msg_queue_t* q;
298: jsval val;
299: char* name=NULL;
300:
301: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
302: JS_ReportError(cx,getprivate_failure,WHERE);
303: return(JS_FALSE);
304: }
305:
306: val = argv[argn++];
307:
308: if(argn < argc)
309: name=JS_GetStringBytes(JS_ValueToString(cx,argv[argn++]));
310:
311: *rval = BOOLEAN_TO_JSVAL(js_enqueue_value(cx, q, val, name));
312:
313: return(JS_TRUE);
314: }
315:
316: /* Queue Object Properites */
317: enum {
318: QUEUE_PROP_NAME
319: ,QUEUE_PROP_DATA_WAITING
320: ,QUEUE_PROP_READ_LEVEL
321: ,QUEUE_PROP_WRITE_LEVEL
322: };
323:
324: #ifdef BUILD_JSDOCS
325: static char* queue_prop_desc[] = {
326: "name of the queue (if it has one)"
327: ,"<i>true</i> if data is waiting to be read from queue"
328: ,"number of values in the read queue"
329: ,"number of values in the write qeueue"
330: ,NULL
331: };
332: #endif
333:
334: static JSBool js_queue_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
335: {
336: jsint tiny;
337: msg_queue_t* q;
338:
339: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
340: JS_ReportError(cx,getprivate_failure,WHERE);
341: return(JS_FALSE);
342: }
343:
344: tiny = JSVAL_TO_INT(id);
345:
346: switch(tiny) {
347: case QUEUE_PROP_NAME:
348: if(q->name!=NULL && q->name[0])
349: *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,q->name));
350: break;
351: case QUEUE_PROP_DATA_WAITING:
352: *vp = BOOLEAN_TO_JSVAL(INT_TO_BOOL(msgQueueReadLevel(q)));
353: break;
354: case QUEUE_PROP_READ_LEVEL:
355: *vp = INT_TO_JSVAL(msgQueueReadLevel(q));
356: break;
357: case QUEUE_PROP_WRITE_LEVEL:
358: *vp = INT_TO_JSVAL(msgQueueWriteLevel(q));
359: break;
360: }
361: return(JS_TRUE);
362: }
363:
364: #define QUEUE_PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY
365:
366: static jsSyncPropertySpec js_queue_properties[] = {
367: /* name ,tinyid ,flags, ver */
368:
369: { "name" ,QUEUE_PROP_NAME ,QUEUE_PROP_FLAGS, 312 },
370: { "data_waiting" ,QUEUE_PROP_DATA_WAITING,QUEUE_PROP_FLAGS, 312 },
371: { "read_level" ,QUEUE_PROP_READ_LEVEL ,QUEUE_PROP_FLAGS, 312 },
372: { "write_level" ,QUEUE_PROP_WRITE_LEVEL ,QUEUE_PROP_FLAGS, 312 },
373: {0}
374: };
375:
376: static JSClass js_queue_class = {
377: "Queue" /* name */
378: ,JSCLASS_HAS_PRIVATE /* flags */
379: ,JS_PropertyStub /* addProperty */
380: ,JS_PropertyStub /* delProperty */
381: ,js_queue_get /* getProperty */
382: ,JS_PropertyStub /* setProperty */
383: ,JS_EnumerateStub /* enumerate */
384: ,JS_ResolveStub /* resolve */
385: ,JS_ConvertStub /* convert */
386: ,js_finalize_queue /* finalize */
387: };
388:
389: static jsSyncMethodSpec js_queue_functions[] = {
390: {"poll", js_poll, 1, JSTYPE_UNDEF, "[timeout=<tt>0</tt>]"
391: ,JSDOCSTR("wait for any value to be written to the queue for up to <i>timeout</i> milliseconds "
392: "(default: <i>0</i>), returns <i>true</i> or the <i>name</i> (string) of "
393: "the value waiting (if it has one), or <i>false</i> if no values are waiting")
394: ,312
395: },
396: {"read", js_read, 1, JSTYPE_UNDEF, "[string name] or [timeout=<tt>0</tt>]"
397: ,JSDOCSTR("read a value from the queue, if <i>name</i> not specified, reads next value "
398: "from the bottom of the queue (waiting up to <i>timeout</i> milliseconds)")
399: ,313
400: },
401: {"peek", js_peek, 1, JSTYPE_UNDEF, "[timeout=<tt>0</tt>]"
402: ,JSDOCSTR("peek at the value at the bottom of the queue, "
403: "wait up to <i>timeout</i> milliseconds for any value to be written "
404: "(default: <i>0</i>)")
405: ,313
406: },
407: {"write", js_write, 1, JSTYPE_BOOLEAN, "value [,name=<i>none</i>]"
408: ,JSDOCSTR("write a value (optionally named) to the queue")
409: ,312
410: },
411: {0}
412: };
413:
414: /* Queue Constructor (creates queue) */
415:
416: static JSBool
417: js_queue_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
418: {
419: uintN argn=0;
420: char* name=NULL;
421: int32 flags=MSG_QUEUE_BIDIR;
422: msg_queue_t* q=NULL;
423: list_node_t* n;
424:
425: *rval = JSVAL_VOID;
426:
427: #if 0 /* This doesn't appear to be doing anything but leaking memory */
428: if((q=(msg_queue_t*)malloc(sizeof(msg_queue_t)))==NULL) {
429: JS_ReportError(cx,"malloc failed");
430: return(JS_FALSE);
431: }
432: memset(q,0,sizeof(msg_queue_t));
433: #endif
434:
435: if(argn<argc && JSVAL_IS_STRING(argv[argn]))
436: name=JS_GetStringBytes(JS_ValueToString(cx,argv[argn++]));
437:
438: if(argn<argc && JSVAL_IS_NUMBER(argv[argn]))
439: JS_ValueToInt32(cx,argv[argn++],&flags);
440:
441: if(name!=NULL) {
442: for(n=listFirstNode(&named_queues);n!=NULL;n=listNextNode(n))
443: if((q=n->data)!=NULL && !stricmp(q->name,name))
444: break;
445: if(n==NULL)
446: q=NULL;
447: }
448:
449: if(q==NULL) {
450: q=msgQueueInit(NULL,flags);
451: if(name!=NULL)
452: SAFECOPY(q->name,name);
453: listPushNode(&named_queues,q);
454: } else
455: msgQueueAttach(q);
456:
457: if(!JS_SetPrivate(cx, obj, q)) {
458: JS_ReportError(cx,"JS_SetPrivate failed");
459: return(JS_FALSE);
460: }
461:
462: if(!js_DefineSyncProperties(cx, obj, js_queue_properties)) {
463: JS_ReportError(cx,"js_DefineSyncProperties failed");
464: return(JS_FALSE);
465: }
466:
467: if(!js_DefineSyncMethods(cx, obj, js_queue_functions, FALSE)) {
468: JS_ReportError(cx,"js_DefineSyncMethods failed");
469: return(JS_FALSE);
470: }
471:
472: #ifdef BUILD_JSDOCS
473: js_DescribeSyncObject(cx,obj,"Class for bi-directional message queues. "
474: "Used for inter-thread/module communications.", 312);
475: js_DescribeSyncConstructor(cx,obj,"To create a new (named) Queue object: "
476: "<tt>var q = new Queue(<i>name</i>)</tt>");
477: js_CreateArrayOfStrings(cx, obj, "_property_desc_list", queue_prop_desc, JSPROP_READONLY);
478: #endif
479:
480: return(JS_TRUE);
481: }
482:
483: JSObject* DLLCALL js_CreateQueueClass(JSContext* cx, JSObject* parent)
484: {
485: JSObject* obj;
486:
487: obj = JS_InitClass(cx, parent, NULL
488: ,&js_queue_class
489: ,js_queue_constructor
490: ,0 /* number of constructor args */
491: ,NULL /* props, specified in constructor */
492: ,NULL /* funcs, specified in constructor */
493: ,NULL
494: ,NULL);
495:
496: return(obj);
497: }
498:
499: JSObject* DLLCALL js_CreateQueueObject(JSContext* cx, JSObject* parent, char *name, msg_queue_t* q)
500: {
501: JSObject* obj;
502:
503: if(name==NULL)
504: obj = JS_NewObject(cx, &js_queue_class, NULL, parent);
505: else
506: obj = JS_DefineObject(cx, parent, name, &js_queue_class, NULL
507: ,JSPROP_ENUMERATE|JSPROP_READONLY);
508:
509: if(obj==NULL)
510: return(NULL);
511:
512: if(!js_DefineSyncProperties(cx, obj, js_queue_properties))
513: return(NULL);
514:
515: if(!JS_SetPrivate(cx, obj, q))
516: return(NULL);
517:
518: if (!js_DefineSyncMethods(cx, obj, js_queue_functions, FALSE))
519: return(NULL);
520:
521: return(obj);
522: }
523:
524:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.