# encoding: ascii
# api: powershell
# title: Get-CpuCache
# description: Retrieves information about cache of logical processors. Original post of gregzakh can be fount at https://github.com/gregzakh/tacitus/blob/master/cpu_cache/source.ps1
# version: 0.1
# type: function
# author: Don Jones
# license: CC0
# function: Get-CpuCache
# x-poshcode-id: 6396
# x-archived: 2016-06-19T09:57:21
# x-published: 2016-06-17T18:21:00
#
#
function Get-CpuCache {
  begin {
    if (($ta = [PSObject].Assembly.GetType(
      'System.Management.Automation.TypeAccelerators'
    ))::Get.Keys -notcontains 'Marshal') {
      $ta::Add('Marshal', [Runtime.InteropServices.Marshal])
    }
    
    Set-Variable ($$ = [Regex].Assembly.GetType(
      'Microsoft.Win32.NativeMethods'
    ).GetMethod('NtQuerySystemInformation')).Name $$
    
    $ret = 0
  }
  process {
    try {
      $ptr = [Marshal]::AllocHGlobal(24)
      
      if ($NtQuerySystemInformation.Invoke(
        $null, ($par = [Object[]]@(73, $ptr, 24, $ret)
      )) -eq 0xC0000004) {
        $ptr = [Marshal]::ReAllocHGlobal($ptr, [IntPtr]$par[3])
        
        if ($NtQuerySystemInformation.Invoke(
          $null, @(73, $ptr, $par[3], 0)
        ) -ne 0) {
          throw New-Object InvalidOperationException(
            'Could not retrieve required data.'
          )
        }
      }
      
      $len = $par[3]
      $tmp = $ptr
      $(for ($i = 0; $i -lt $len; $i += $len / 24) {
        if ([Marshal]::ReadInt32($tmp, 4) -eq 2) {
          [Byte[]]$bytes = 0..11 | ForEach-Object {
            $ofb = 8
            $CACHE_TYPE = @{
              0 = 'CacheUnified'
              1 = 'CacheInstruction'
              2 = 'CacheData'
              3 = 'CacheTrace'
            }
          }{
            [Marshal]::ReadByte($tmp, $ofb)
            $ofb++
          }
          New-Object PSObject -Property @{
            Level = $bytes[0]
            Associativity = $bytes[1]
            LineSize = [BitConverter]::ToInt16($bytes[2..3], 0)
            Size = [BitConverter]::ToInt32($bytes[4..7], 0)
            Type = [BitConverter]::ToInt32($bytes[8..15], 0)
          } | Select-Object @{
            N='Type';E={$CACHE_TYPE.Item($_.Type)}
          }, Level, @{
            N='Size(KB)';E={$_.Size / 1Kb}
          }, Associativity, LineSize
        }
        $tmp = [IntPtr]($tmp.ToInt32() + 24)
      }) | Format-Table -AutoSize
    }
    catch { $_.Exception }
    finally {
      if ($ptr) { [Marshal]::FreeHGlobal($ptr) }
    }
  }
  end {
    [void]$ta::Remove('Marshal')
  }
}
