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