1 /*
2  * stats.c
3  */
4 
5 /*
6  *  Copyright (C) 2002 RealVNC Ltd.
7  *  OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
8  *  Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
9  *  All Rights Reserved.
10  *
11  *  This is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This software is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this software; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
24  *  USA.
25  */
26 
27 #include <rfb/rfb.h>
28 
29 char *messageNameServer2Client(uint32_t type, char *buf, int len);
30 char *messageNameClient2Server(uint32_t type, char *buf, int len);
31 char *encodingName(uint32_t enc, char *buf, int len);
32 
33 rfbStatList *rfbStatLookupEncoding(rfbClientPtr cl, uint32_t type);
34 rfbStatList *rfbStatLookupMessage(rfbClientPtr cl, uint32_t type);
35 
36 void  rfbStatRecordEncodingSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw);
37 void  rfbStatRecordEncodingRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw);
38 void  rfbStatRecordMessageSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw);
39 void  rfbStatRecordMessageRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw);
40 void rfbResetStats(rfbClientPtr cl);
41 void rfbPrintStats(rfbClientPtr cl);
42 
43 
44 
45 
messageNameServer2Client(uint32_t type,char * buf,int len)46 char *messageNameServer2Client(uint32_t type, char *buf, int len) {
47     if (buf==NULL) return "error";
48     switch (type) {
49     case rfbFramebufferUpdate:        snprintf(buf, len, "FramebufferUpdate"); break;
50     case rfbSetColourMapEntries:      snprintf(buf, len, "SetColourMapEntries"); break;
51     case rfbBell:                     snprintf(buf, len, "Bell"); break;
52     case rfbServerCutText:            snprintf(buf, len, "ServerCutText"); break;
53     case rfbResizeFrameBuffer:        snprintf(buf, len, "ResizeFrameBuffer"); break;
54     case rfbFileTransfer:             snprintf(buf, len, "FileTransfer"); break;
55     case rfbTextChat:                 snprintf(buf, len, "TextChat"); break;
56     case rfbPalmVNCReSizeFrameBuffer: snprintf(buf, len, "PalmVNCReSize"); break;
57     case rfbXvp:                      snprintf(buf, len, "XvpServerMessage"); break;
58     default:
59         snprintf(buf, len, "svr2cli-0x%08X", 0xFF);
60     }
61     return buf;
62 }
63 
messageNameClient2Server(uint32_t type,char * buf,int len)64 char *messageNameClient2Server(uint32_t type, char *buf, int len) {
65     if (buf==NULL) return "error";
66     switch (type) {
67     case rfbSetPixelFormat:           snprintf(buf, len, "SetPixelFormat"); break;
68     case rfbFixColourMapEntries:      snprintf(buf, len, "FixColourMapEntries"); break;
69     case rfbSetEncodings:             snprintf(buf, len, "SetEncodings"); break;
70     case rfbFramebufferUpdateRequest: snprintf(buf, len, "FramebufferUpdate"); break;
71     case rfbKeyEvent:                 snprintf(buf, len, "KeyEvent"); break;
72     case rfbPointerEvent:             snprintf(buf, len, "PointerEvent"); break;
73     case rfbClientCutText:            snprintf(buf, len, "ClientCutText"); break;
74     case rfbFileTransfer:             snprintf(buf, len, "FileTransfer"); break;
75     case rfbSetScale:                 snprintf(buf, len, "SetScale"); break;
76     case rfbSetServerInput:           snprintf(buf, len, "SetServerInput"); break;
77     case rfbSetSW:                    snprintf(buf, len, "SetSingleWindow"); break;
78     case rfbTextChat:                 snprintf(buf, len, "TextChat"); break;
79     case rfbPalmVNCSetScaleFactor:    snprintf(buf, len, "PalmVNCSetScale"); break;
80     case rfbXvp:                      snprintf(buf, len, "XvpClientMessage"); break;
81     case rfbSetDesktopSize:           snprintf(buf, len, "SetDesktopSize"); break;
82     default:
83         snprintf(buf, len, "cli2svr-0x%08X", type);
84 
85 
86     }
87     return buf;
88 }
89 
90 /* Encoding name must be <=16 characters to fit nicely on the status output in
91  * an 80 column terminal window
92  */
encodingName(uint32_t type,char * buf,int len)93 char *encodingName(uint32_t type, char *buf, int len) {
94     if (buf==NULL) return "error";
95 
96     switch (type) {
97     case rfbEncodingRaw:                snprintf(buf, len, "raw");         break;
98     case rfbEncodingCopyRect:           snprintf(buf, len, "copyRect");    break;
99     case rfbEncodingRRE:                snprintf(buf, len, "RRE");         break;
100     case rfbEncodingCoRRE:              snprintf(buf, len, "CoRRE");       break;
101     case rfbEncodingHextile:            snprintf(buf, len, "hextile");     break;
102     case rfbEncodingZlib:               snprintf(buf, len, "zlib");        break;
103     case rfbEncodingTight:              snprintf(buf, len, "tight");       break;
104     case rfbEncodingTightPng:           snprintf(buf, len, "tightPng");    break;
105     case rfbEncodingZlibHex:            snprintf(buf, len, "zlibhex");     break;
106     case rfbEncodingUltra:              snprintf(buf, len, "ultra");       break;
107     case rfbEncodingZRLE:               snprintf(buf, len, "ZRLE");        break;
108     case rfbEncodingZYWRLE:             snprintf(buf, len, "ZYWRLE");      break;
109     case rfbEncodingCache:              snprintf(buf, len, "cache");       break;
110     case rfbEncodingCacheEnable:        snprintf(buf, len, "cacheEnable"); break;
111     case rfbEncodingXOR_Zlib:           snprintf(buf, len, "xorZlib");     break;
112     case rfbEncodingXORMonoColor_Zlib:  snprintf(buf, len, "xorMonoZlib");  break;
113     case rfbEncodingXORMultiColor_Zlib: snprintf(buf, len, "xorColorZlib"); break;
114     case rfbEncodingSolidColor:         snprintf(buf, len, "solidColor");  break;
115     case rfbEncodingXOREnable:          snprintf(buf, len, "xorEnable");   break;
116     case rfbEncodingCacheZip:           snprintf(buf, len, "cacheZip");    break;
117     case rfbEncodingSolMonoZip:         snprintf(buf, len, "monoZip");     break;
118     case rfbEncodingUltraZip:           snprintf(buf, len, "ultraZip");    break;
119 
120     case rfbEncodingXCursor:            snprintf(buf, len, "Xcursor");     break;
121     case rfbEncodingRichCursor:         snprintf(buf, len, "RichCursor");  break;
122     case rfbEncodingPointerPos:         snprintf(buf, len, "PointerPos");  break;
123 
124     case rfbEncodingLastRect:           snprintf(buf, len, "LastRect");    break;
125     case rfbEncodingNewFBSize:          snprintf(buf, len, "NewFBSize");   break;
126     case rfbEncodingExtDesktopSize:     snprintf(buf, len, "ExtendedDesktopSize"); break;
127     case rfbEncodingKeyboardLedState:   snprintf(buf, len, "LedState");    break;
128     case rfbEncodingSupportedMessages:  snprintf(buf, len, "SupportedMessage");  break;
129     case rfbEncodingSupportedEncodings: snprintf(buf, len, "SupportedEncoding"); break;
130     case rfbEncodingServerIdentity:     snprintf(buf, len, "ServerIdentify");    break;
131 
132     /* The following lookups do not report in stats */
133     case rfbEncodingCompressLevel0: snprintf(buf, len, "CompressLevel0");  break;
134     case rfbEncodingCompressLevel1: snprintf(buf, len, "CompressLevel1");  break;
135     case rfbEncodingCompressLevel2: snprintf(buf, len, "CompressLevel2");  break;
136     case rfbEncodingCompressLevel3: snprintf(buf, len, "CompressLevel3");  break;
137     case rfbEncodingCompressLevel4: snprintf(buf, len, "CompressLevel4");  break;
138     case rfbEncodingCompressLevel5: snprintf(buf, len, "CompressLevel5");  break;
139     case rfbEncodingCompressLevel6: snprintf(buf, len, "CompressLevel6");  break;
140     case rfbEncodingCompressLevel7: snprintf(buf, len, "CompressLevel7");  break;
141     case rfbEncodingCompressLevel8: snprintf(buf, len, "CompressLevel8");  break;
142     case rfbEncodingCompressLevel9: snprintf(buf, len, "CompressLevel9");  break;
143 
144     case rfbEncodingQualityLevel0:  snprintf(buf, len, "QualityLevel0");   break;
145     case rfbEncodingQualityLevel1:  snprintf(buf, len, "QualityLevel1");   break;
146     case rfbEncodingQualityLevel2:  snprintf(buf, len, "QualityLevel2");   break;
147     case rfbEncodingQualityLevel3:  snprintf(buf, len, "QualityLevel3");   break;
148     case rfbEncodingQualityLevel4:  snprintf(buf, len, "QualityLevel4");   break;
149     case rfbEncodingQualityLevel5:  snprintf(buf, len, "QualityLevel5");   break;
150     case rfbEncodingQualityLevel6:  snprintf(buf, len, "QualityLevel6");   break;
151     case rfbEncodingQualityLevel7:  snprintf(buf, len, "QualityLevel7");   break;
152     case rfbEncodingQualityLevel8:  snprintf(buf, len, "QualityLevel8");   break;
153     case rfbEncodingQualityLevel9:  snprintf(buf, len, "QualityLevel9");   break;
154 
155 
156     default:
157         snprintf(buf, len, "Enc(0x%08X)", type);
158     }
159 
160     return buf;
161 }
162 
163 
164 
165 
166 
rfbStatLookupEncoding(rfbClientPtr cl,uint32_t type)167 rfbStatList *rfbStatLookupEncoding(rfbClientPtr cl, uint32_t type)
168 {
169     rfbStatList *ptr;
170     if (cl==NULL) return NULL;
171     for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
172     {
173         if (ptr->type==type) return ptr;
174     }
175     /* Well, we are here... need to *CREATE* an entry */
176     ptr = (rfbStatList *)malloc(sizeof(rfbStatList));
177     if (ptr!=NULL)
178     {
179         memset((char *)ptr, 0, sizeof(rfbStatList));
180         ptr->type = type;
181         /* add to the top of the list */
182         ptr->Next = cl->statEncList;
183         cl->statEncList = ptr;
184     }
185     return ptr;
186 }
187 
188 
rfbStatLookupMessage(rfbClientPtr cl,uint32_t type)189 rfbStatList *rfbStatLookupMessage(rfbClientPtr cl, uint32_t type)
190 {
191     rfbStatList *ptr;
192     if (cl==NULL) return NULL;
193     for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
194     {
195         if (ptr->type==type) return ptr;
196     }
197     /* Well, we are here... need to *CREATE* an entry */
198     ptr = (rfbStatList *)malloc(sizeof(rfbStatList));
199     if (ptr!=NULL)
200     {
201         memset((char *)ptr, 0, sizeof(rfbStatList));
202         ptr->type = type;
203         /* add to the top of the list */
204         ptr->Next = cl->statMsgList;
205         cl->statMsgList = ptr;
206     }
207     return ptr;
208 }
209 
rfbStatRecordEncodingSentAdd(rfbClientPtr cl,uint32_t type,int byteCount)210 void rfbStatRecordEncodingSentAdd(rfbClientPtr cl, uint32_t type, int byteCount) /* Specifically for tight encoding */
211 {
212     rfbStatList *ptr;
213 
214     ptr = rfbStatLookupEncoding(cl, type);
215     if (ptr!=NULL)
216         ptr->bytesSent      += byteCount;
217 }
218 
219 
rfbStatRecordEncodingSent(rfbClientPtr cl,uint32_t type,int byteCount,int byteIfRaw)220 void  rfbStatRecordEncodingSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw)
221 {
222     rfbStatList *ptr;
223 
224     ptr = rfbStatLookupEncoding(cl, type);
225     if (ptr!=NULL)
226     {
227         ptr->sentCount++;
228         ptr->bytesSent      += byteCount;
229         ptr->bytesSentIfRaw += byteIfRaw;
230     }
231 }
232 
rfbStatRecordEncodingRcvd(rfbClientPtr cl,uint32_t type,int byteCount,int byteIfRaw)233 void  rfbStatRecordEncodingRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw)
234 {
235     rfbStatList *ptr;
236 
237     ptr = rfbStatLookupEncoding(cl, type);
238     if (ptr!=NULL)
239     {
240         ptr->rcvdCount++;
241         ptr->bytesRcvd      += byteCount;
242         ptr->bytesRcvdIfRaw += byteIfRaw;
243     }
244 }
245 
rfbStatRecordMessageSent(rfbClientPtr cl,uint32_t type,int byteCount,int byteIfRaw)246 void  rfbStatRecordMessageSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw)
247 {
248     rfbStatList *ptr;
249 
250     ptr = rfbStatLookupMessage(cl, type);
251     if (ptr!=NULL)
252     {
253         ptr->sentCount++;
254         ptr->bytesSent      += byteCount;
255         ptr->bytesSentIfRaw += byteIfRaw;
256     }
257 }
258 
rfbStatRecordMessageRcvd(rfbClientPtr cl,uint32_t type,int byteCount,int byteIfRaw)259 void  rfbStatRecordMessageRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw)
260 {
261     rfbStatList *ptr;
262 
263     ptr = rfbStatLookupMessage(cl, type);
264     if (ptr!=NULL)
265     {
266         ptr->rcvdCount++;
267         ptr->bytesRcvd      += byteCount;
268         ptr->bytesRcvdIfRaw += byteIfRaw;
269     }
270 }
271 
272 
rfbStatGetSentBytes(rfbClientPtr cl)273 int rfbStatGetSentBytes(rfbClientPtr cl)
274 {
275     rfbStatList *ptr=NULL;
276     int bytes=0;
277     if (cl==NULL) return 0;
278     for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
279         bytes += ptr->bytesSent;
280     for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
281         bytes += ptr->bytesSent;
282     return bytes;
283 }
284 
rfbStatGetSentBytesIfRaw(rfbClientPtr cl)285 int rfbStatGetSentBytesIfRaw(rfbClientPtr cl)
286 {
287     rfbStatList *ptr=NULL;
288     int bytes=0;
289     if (cl==NULL) return 0;
290     for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
291         bytes += ptr->bytesSentIfRaw;
292     for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
293         bytes += ptr->bytesSentIfRaw;
294     return bytes;
295 }
296 
rfbStatGetRcvdBytes(rfbClientPtr cl)297 int rfbStatGetRcvdBytes(rfbClientPtr cl)
298 {
299     rfbStatList *ptr=NULL;
300     int bytes=0;
301     if (cl==NULL) return 0;
302     for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
303         bytes += ptr->bytesRcvd;
304     for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
305         bytes += ptr->bytesRcvd;
306     return bytes;
307 }
308 
rfbStatGetRcvdBytesIfRaw(rfbClientPtr cl)309 int rfbStatGetRcvdBytesIfRaw(rfbClientPtr cl)
310 {
311     rfbStatList *ptr=NULL;
312     int bytes=0;
313     if (cl==NULL) return 0;
314     for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
315         bytes += ptr->bytesRcvdIfRaw;
316     for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
317         bytes += ptr->bytesRcvdIfRaw;
318     return bytes;
319 }
320 
rfbStatGetMessageCountSent(rfbClientPtr cl,uint32_t type)321 int rfbStatGetMessageCountSent(rfbClientPtr cl, uint32_t type)
322 {
323   rfbStatList *ptr=NULL;
324     if (cl==NULL) return 0;
325   for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
326       if (ptr->type==type) return ptr->sentCount;
327   return 0;
328 }
rfbStatGetMessageCountRcvd(rfbClientPtr cl,uint32_t type)329 int rfbStatGetMessageCountRcvd(rfbClientPtr cl, uint32_t type)
330 {
331   rfbStatList *ptr=NULL;
332     if (cl==NULL) return 0;
333   for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
334       if (ptr->type==type) return ptr->rcvdCount;
335   return 0;
336 }
337 
rfbStatGetEncodingCountSent(rfbClientPtr cl,uint32_t type)338 int rfbStatGetEncodingCountSent(rfbClientPtr cl, uint32_t type)
339 {
340   rfbStatList *ptr=NULL;
341     if (cl==NULL) return 0;
342   for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
343       if (ptr->type==type) return ptr->sentCount;
344   return 0;
345 }
rfbStatGetEncodingCountRcvd(rfbClientPtr cl,uint32_t type)346 int rfbStatGetEncodingCountRcvd(rfbClientPtr cl, uint32_t type)
347 {
348   rfbStatList *ptr=NULL;
349     if (cl==NULL) return 0;
350   for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
351       if (ptr->type==type) return ptr->rcvdCount;
352   return 0;
353 }
354 
355 
356 
357 
rfbResetStats(rfbClientPtr cl)358 void rfbResetStats(rfbClientPtr cl)
359 {
360     rfbStatList *ptr;
361     if (cl==NULL) return;
362     while (cl->statEncList!=NULL)
363     {
364         ptr = cl->statEncList;
365         cl->statEncList = ptr->Next;
366         free(ptr);
367     }
368     while (cl->statMsgList!=NULL)
369     {
370         ptr = cl->statMsgList;
371         cl->statMsgList = ptr->Next;
372         free(ptr);
373     }
374 }
375 
376 
rfbPrintStats(rfbClientPtr cl)377 void rfbPrintStats(rfbClientPtr cl)
378 {
379     rfbStatList *ptr=NULL;
380     char encBuf[64];
381     double savings=0.0;
382     int    totalRects=0;
383     double totalBytes=0.0;
384     double totalBytesIfRaw=0.0;
385 
386     char *name=NULL;
387     int bytes=0;
388     int bytesIfRaw=0;
389     int count=0;
390 
391     if (cl==NULL) return;
392 
393     rfbLog("%-21.21s  %-6.6s   %9.9s/%9.9s (%6.6s)\n", "Statistics", "events", "Transmit","RawEquiv","saved");
394     for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
395     {
396         name       = messageNameServer2Client(ptr->type, encBuf, sizeof(encBuf));
397         count      = ptr->sentCount;
398         bytes      = ptr->bytesSent;
399         bytesIfRaw = ptr->bytesSentIfRaw;
400 
401         savings = 0.0;
402         if (bytesIfRaw>0.0)
403             savings = 100.0 - (((double)bytes / (double)bytesIfRaw) * 100.0);
404         if ((bytes>0) || (count>0) || (bytesIfRaw>0))
405             rfbLog(" %-20.20s: %6d | %9d/%9d (%5.1f%%)\n",
406 	        name, count, bytes, bytesIfRaw, savings);
407         totalRects += count;
408         totalBytes += bytes;
409         totalBytesIfRaw += bytesIfRaw;
410     }
411 
412     for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
413     {
414         name       = encodingName(ptr->type, encBuf, sizeof(encBuf));
415         count      = ptr->sentCount;
416         bytes      = ptr->bytesSent;
417         bytesIfRaw = ptr->bytesSentIfRaw;
418         savings    = 0.0;
419 
420         if (bytesIfRaw>0.0)
421             savings = 100.0 - (((double)bytes / (double)bytesIfRaw) * 100.0);
422         if ((bytes>0) || (count>0) || (bytesIfRaw>0))
423             rfbLog(" %-20.20s: %6d | %9d/%9d (%5.1f%%)\n",
424 	        name, count, bytes, bytesIfRaw, savings);
425         totalRects += count;
426         totalBytes += bytes;
427         totalBytesIfRaw += bytesIfRaw;
428     }
429     savings=0.0;
430     if (totalBytesIfRaw>0.0)
431         savings = 100.0 - ((totalBytes/totalBytesIfRaw)*100.0);
432     rfbLog(" %-20.20s: %6d | %9.0f/%9.0f (%5.1f%%)\n",
433             "TOTALS", totalRects, totalBytes,totalBytesIfRaw, savings);
434 
435     totalRects=0.0;
436     totalBytes=0.0;
437     totalBytesIfRaw=0.0;
438 
439     rfbLog("%-21.21s  %-6.6s   %9.9s/%9.9s (%6.6s)\n", "Statistics", "events", "Received","RawEquiv","saved");
440     for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next)
441     {
442         name       = messageNameClient2Server(ptr->type, encBuf, sizeof(encBuf));
443         count      = ptr->rcvdCount;
444         bytes      = ptr->bytesRcvd;
445         bytesIfRaw = ptr->bytesRcvdIfRaw;
446         savings    = 0.0;
447 
448         if (bytesIfRaw>0.0)
449             savings = 100.0 - (((double)bytes / (double)bytesIfRaw) * 100.0);
450         if ((bytes>0) || (count>0) || (bytesIfRaw>0))
451             rfbLog(" %-20.20s: %6d | %9d/%9d (%5.1f%%)\n",
452 	        name, count, bytes, bytesIfRaw, savings);
453         totalRects += count;
454         totalBytes += bytes;
455         totalBytesIfRaw += bytesIfRaw;
456     }
457     for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next)
458     {
459         name       = encodingName(ptr->type, encBuf, sizeof(encBuf));
460         count      = ptr->rcvdCount;
461         bytes      = ptr->bytesRcvd;
462         bytesIfRaw = ptr->bytesRcvdIfRaw;
463         savings    = 0.0;
464 
465         if (bytesIfRaw>0.0)
466             savings = 100.0 - (((double)bytes / (double)bytesIfRaw) * 100.0);
467         if ((bytes>0) || (count>0) || (bytesIfRaw>0))
468             rfbLog(" %-20.20s: %6d | %9d/%9d (%5.1f%%)\n",
469 	        name, count, bytes, bytesIfRaw, savings);
470         totalRects += count;
471         totalBytes += bytes;
472         totalBytesIfRaw += bytesIfRaw;
473     }
474     savings=0.0;
475     if (totalBytesIfRaw>0.0)
476         savings = 100.0 - ((totalBytes/totalBytesIfRaw)*100.0);
477     rfbLog(" %-20.20s: %6d | %9.0f/%9.0f (%5.1f%%)\n",
478             "TOTALS", totalRects, totalBytes,totalBytesIfRaw, savings);
479 
480 }
481 
482