MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.Diagnostics;7 8namespace Microsoft.PowerShell.Commands.Internal.Format9{10 /// <summary>11 /// Queue to provide sliding window capabilities for auto size functionality12 /// It provides caching capabilities (either the first N objects in a group13 /// or all the objects in a group)14 /// </summary>15 internal sealed class OutputGroupQueue16 {17 /// <summary>18 /// Create a grouping cache.19 /// </summary>20 /// <param name="callBack">Notification callback to be called when the desired number of objects is reached.</param>21 /// <param name="objectCount">Max number of objects to be cached.</param>22 internal OutputGroupQueue(FormattedObjectsCache.ProcessCachedGroupNotification callBack, int objectCount)23 {24 _notificationCallBack = callBack;25 _objectCount = objectCount;26 }27 28 /// <summary>29 /// Create a time-bounded grouping cache.30 /// </summary>31 /// <param name="callBack">Notification callback to be called when the desired number of objects is reached.</param>32 /// <param name="groupingDuration">Max amount of time to cache of objects.</param>33 internal OutputGroupQueue(FormattedObjectsCache.ProcessCachedGroupNotification callBack, TimeSpan groupingDuration)34 {35 _notificationCallBack = callBack;36 _groupingDuration = groupingDuration;37 }38 39 /// <summary>40 /// Add an object to the cache.41 /// </summary>42 /// <param name="o">Object to add.</param>43 /// <returns>Objects the cache needs to return. It can be null.</returns>44 internal List<PacketInfoData> Add(PacketInfoData o)45 {46 if (o is FormatStartData fsd)47 {48 // just cache the reference (used during the notification call)49 _formatStartData = fsd;50 }51 52 UpdateObjectCount(o);53 54 // STATE TRANSITION: we are not processing and we start55 if (!_processingGroup && (o is GroupStartData))56 {57 // just set the flag and start caching58 _processingGroup = true;59 _currentObjectCount = 0;60 61 if (_groupingDuration > TimeSpan.MinValue)62 {63 _groupingTimer = Stopwatch.StartNew();64 }65 66 _queue.Enqueue(o);67 return null;68 }69 70 // STATE TRANSITION: we are processing and we stop71 if (_processingGroup &&72 ((o is GroupEndData) ||73 (_objectCount > 0) && (_currentObjectCount >= _objectCount)) ||74 ((_groupingTimer != null) && (_groupingTimer.Elapsed > _groupingDuration))75 )76 {77 // reset the object count78 _currentObjectCount = 0;79 80 if (_groupingTimer != null)81 {82 _groupingTimer.Stop();83 _groupingTimer = null;84 }85 86 // add object to queue, to be picked up87 _queue.Enqueue(o);88 89 // we are at the end of a group, drain the queue90 Notify();91 _processingGroup = false;92 93 List<PacketInfoData> retVal = new List<PacketInfoData>();94 95 while (_queue.Count > 0)96 {97 retVal.Add(_queue.Dequeue());98 }99 100 return retVal;101 }102 103 // NO STATE TRANSITION: check the state we are in104 if (_processingGroup)105 {106 // we are in the caching state107 _queue.Enqueue(o);108 return null;109 }110 111 // we are not processing, so just return it112 List<PacketInfoData> ret = new List<PacketInfoData>();113 114 ret.Add(o);115 return ret;116 }117 118 private void UpdateObjectCount(PacketInfoData o)119 {120 // add only of it's not a control message121 // and it's not out of band122 if (o is FormatEntryData fed && !fed.outOfBand)123 {124 _currentObjectCount++;125 }126 }127 128 private void Notify()129 {130 if (_notificationCallBack == null)131 return;132 133 // filter out the out of band data, since they do not participate in the134 // auto resize algorithm135 List<PacketInfoData> validObjects = new List<PacketInfoData>();136 137 foreach (PacketInfoData x in _queue)138 {139 if (x is FormatEntryData fed && fed.outOfBand)140 continue;141 142 validObjects.Add(x);143 }144 145 _notificationCallBack(_formatStartData, validObjects);146 }147 148 /// <summary>149 /// Remove a single object from the queue.150 /// </summary>151 /// <returns>Object retrieved, null if queue is empty.</returns>152 internal PacketInfoData Dequeue()153 {154 if (_queue.Count == 0)155 return null;156 157 return _queue.Dequeue();158 }159 160 /// <summary>161 /// Queue to store the currently cached objects.162 /// </summary>163 private readonly Queue<PacketInfoData> _queue = new Queue<PacketInfoData>();164 165 /// <summary>166 /// Number of objects to compute the best fit.167 /// Zero: all the objects168 /// a positive number N: use the first N.169 /// </summary>170 private readonly int _objectCount = 0;171 172 /// <summary>173 /// Maximum amount of time for record processing to compute the best fit.174 /// MaxValue: all the objects.175 /// A positive timespan: use all objects that have been processed within the timeframe.176 /// </summary>177 private readonly TimeSpan _groupingDuration = TimeSpan.MinValue;178 private Stopwatch _groupingTimer = null;179 180 /// <summary>181 /// Notification callback to be called when we have accumulated enough182 /// data to compute a hint.183 /// </summary>184 private readonly FormattedObjectsCache.ProcessCachedGroupNotification _notificationCallBack = null;185 186 /// <summary>187 /// Reference kept to be used during notification.188 /// </summary>189 private FormatStartData _formatStartData = null;190 191 /// <summary>192 /// State flag to signal we are queuing.193 /// </summary>194 private bool _processingGroup = false;195 196 /// <summary>197 /// Current object count.198 /// </summary>199 private int _currentObjectCount = 0;200 }201 202 /// <summary>203 /// Facade class managing the front end and the autosize cache.204 /// </summary>205 internal sealed class FormattedObjectsCache206 {207 /// <summary>208 /// Delegate to allow notifications when the autosize queue is about to be drained.209 /// </summary>210 /// <param name="formatStartData">Current Fs control message.</param>211 /// <param name="objects">Enumeration of PacketInfoData objects.</param>212 internal delegate void ProcessCachedGroupNotification(FormatStartData formatStartData, List<PacketInfoData> objects);213 214 /// <summary>215 /// Decide right away if we need a front end cache (e.g. printing)216 /// </summary>217 /// <param name="cacheFrontEnd">If true, create a front end cache object.</param>218 internal FormattedObjectsCache(bool cacheFrontEnd)219 {220 if (cacheFrontEnd)221 _frontEndQueue = new Queue<PacketInfoData>();222 }223 224 /// <summary>225 /// If needed, add a back end autosize (grouping) cache.226 /// </summary>227 /// <param name="callBack">Notification callback to be called when the desired number of objects is reached.</param>228 /// <param name="objectCount">Max number of objects to be cached.</param>229 internal void EnableGroupCaching(ProcessCachedGroupNotification callBack, int objectCount)230 {231 if (callBack != null)232 _groupQueue = new OutputGroupQueue(callBack, objectCount);233 }234 235 /// <summary>236 /// If needed, add a back end autosize (grouping) cache.237 /// </summary>238 /// <param name="callBack">Notification callback to be called when the desired number of objects is reached.</param>239 /// <param name="groupingDuration">Max amount of time to cache of objects.</param>240 internal void EnableGroupCaching(ProcessCachedGroupNotification callBack, TimeSpan groupingDuration)241 {242 if (callBack != null)243 _groupQueue = new OutputGroupQueue(callBack, groupingDuration);244 }245 246 /// <summary>247 /// Add an object to the cache. the behavior depends on the object added, the248 /// objects already in the cache and the cache settings.249 /// </summary>250 /// <param name="o">Object to add.</param>251 /// <returns>List of objects the cache is flushing.</returns>252 internal List<PacketInfoData> Add(PacketInfoData o)253 {254 // if neither there, pass thru255 if (_frontEndQueue == null && _groupQueue == null)256 {257 List<PacketInfoData> retVal = new List<PacketInfoData>();258 retVal.Add(o);259 return retVal;260 }261 262 // if front present, add to front263 if (_frontEndQueue != null)264 {265 _frontEndQueue.Enqueue(o);266 return null;267 }268 269 // if back only, add to back270 return _groupQueue.Add(o);271 }272 273 /// <summary>274 /// Remove all the objects from the cache.275 /// </summary>276 /// <returns>All the objects that were in the cache.</returns>277 internal List<PacketInfoData> Drain()278 {279 // if neither there,we did not cache at all280 if (_frontEndQueue == null && _groupQueue == null)281 {282 return null;283 }284 285 List<PacketInfoData> retVal = new List<PacketInfoData>();286 287 if (_frontEndQueue != null)288 {289 if (_groupQueue == null)290 {291 // drain the front queue and return the data292 while (_frontEndQueue.Count > 0)293 retVal.Add(_frontEndQueue.Dequeue());294 295 return retVal;296 }297 298 // move from the front to the back queue299 while (_frontEndQueue.Count > 0)300 {301 List<PacketInfoData> groupQueueOut = _groupQueue.Add(_frontEndQueue.Dequeue());302 303 if (groupQueueOut != null)304 foreach (PacketInfoData x in groupQueueOut)305 retVal.Add(x);306 }307 }308 309 // drain the back queue310 while (true)311 {312 PacketInfoData obj = _groupQueue.Dequeue();313 314 if (obj == null)315 break;316 317 retVal.Add(obj);318 }319 320 return retVal;321 }322 323 /// <summary>324 /// Front end queue (if present, cache ALL, if not, bypass)325 /// </summary>326 private readonly Queue<PacketInfoData> _frontEndQueue;327 328 /// <summary>329 /// Back end grouping queue.330 /// </summary>331 private OutputGroupQueue _groupQueue = null;332 }333}334 