github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/kbfsblock/quota.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package kbfsblock 6 7 import ( 8 "time" 9 10 "github.com/keybase/client/go/kbfs/kbfscodec" 11 "github.com/keybase/client/go/protocol/keybase1" 12 ) 13 14 // UsageType indicates the type of usage that quota manager is keeping stats of 15 type UsageType int 16 17 const ( 18 // UsageWrite indicates a data block is written (written blocks include archived blocks) 19 UsageWrite UsageType = iota 20 // UsageArchive indicates an existing (data) block is archived 21 UsageArchive 22 // UsageRead indicates a block is read 23 UsageRead 24 // UsageMDWrite indicates a MD block is written 25 UsageMDWrite 26 // UsageGitWrite indicates a git block is written 27 UsageGitWrite 28 // UsageGitArchive indicates an existing git block is archived 29 UsageGitArchive 30 // NumUsage indicates the number of usage types 31 NumUsage 32 ) 33 34 // UsageStat tracks the amount of bytes/blocks used, broken down by usage types 35 type UsageStat struct { 36 Bytes map[UsageType]int64 37 Blocks map[UsageType]int64 38 // Mtime is in unix nanoseconds 39 Mtime int64 40 } 41 42 // NewUsageStat creates a new UsageStat 43 func NewUsageStat() *UsageStat { 44 return &UsageStat{ 45 Bytes: make(map[UsageType]int64), 46 Blocks: make(map[UsageType]int64), 47 } 48 } 49 50 // UsageStatFromProtocol converts the RPC format into the local format. 51 func UsageStatFromProtocol(stat keybase1.UsageStat) *UsageStat { 52 u := &UsageStat{ 53 Bytes: map[UsageType]int64{ 54 UsageWrite: stat.Bytes.Write, 55 UsageArchive: stat.Bytes.Archive, 56 UsageRead: stat.Bytes.Read, 57 UsageMDWrite: stat.Bytes.MdWrite, 58 UsageGitWrite: stat.Bytes.GitWrite, 59 UsageGitArchive: stat.Bytes.GitArchive, 60 }, 61 Blocks: map[UsageType]int64{ 62 UsageWrite: stat.Blocks.Write, 63 UsageArchive: stat.Blocks.Archive, 64 UsageRead: stat.Blocks.Read, 65 UsageMDWrite: stat.Blocks.MdWrite, 66 UsageGitWrite: stat.Blocks.GitWrite, 67 UsageGitArchive: stat.Blocks.GitArchive, 68 }, 69 Mtime: keybase1.FromTime(stat.Mtime).UnixNano(), 70 } 71 72 return u 73 } 74 75 // NonZero checks whether UsageStat has accumulated any usage info 76 func (u *UsageStat) NonZero() bool { 77 for i := UsageType(0); i < NumUsage; i++ { 78 if u.Bytes[i] != 0 { 79 return true 80 } 81 } 82 return false 83 } 84 85 // ToProtocol converts this stat to the RPC protocol format. 86 func (u *UsageStat) ToProtocol() (res keybase1.UsageStat) { 87 res.Mtime = keybase1.ToTime(time.Unix(0, u.Mtime)) 88 89 res.Bytes.Write = u.Bytes[UsageWrite] 90 res.Bytes.Archive = u.Bytes[UsageArchive] 91 res.Bytes.Read = u.Bytes[UsageRead] 92 res.Bytes.MdWrite = u.Bytes[UsageMDWrite] 93 res.Bytes.GitWrite = u.Bytes[UsageGitWrite] 94 res.Bytes.GitArchive = u.Bytes[UsageGitArchive] 95 96 res.Blocks.Write = u.Blocks[UsageWrite] 97 res.Blocks.Archive = u.Blocks[UsageArchive] 98 res.Blocks.Read = u.Blocks[UsageRead] 99 res.Blocks.MdWrite = u.Blocks[UsageMDWrite] 100 res.Blocks.GitWrite = u.Blocks[UsageGitWrite] 101 res.Blocks.GitArchive = u.Blocks[UsageGitArchive] 102 103 return res 104 } 105 106 // AccumOne records the usage of one block, whose size is denoted by change 107 // A positive change means the block is newly added, negative means the block 108 // is deleted. If archive is true, it means the block is archived. 109 func (u *UsageStat) AccumOne(change int, usage UsageType) { 110 if change == 0 { 111 return 112 } 113 if usage == UsageMDWrite || usage >= NumUsage { 114 return 115 } 116 u.Bytes[usage] += int64(change) 117 if change > 0 { 118 u.Blocks[usage]++ 119 } else { 120 u.Blocks[usage]-- 121 } 122 } 123 124 // Accum combines changes to the existing QuotaInfo object using accumulation function accumF. 125 func (u *UsageStat) Accum(another *UsageStat, accumF func(int64, int64) int64) { 126 if another == nil { 127 return 128 } 129 for k, v := range another.Bytes { 130 u.Bytes[k] = accumF(u.Bytes[k], v) 131 } 132 for k, v := range another.Blocks { 133 u.Blocks[k] = accumF(u.Blocks[k], v) 134 } 135 } 136 137 // QuotaInfo contains a user's quota usage information 138 type QuotaInfo struct { 139 Folders map[string]*UsageStat 140 Total *UsageStat 141 Limit int64 142 GitLimit int64 143 } 144 145 // NewQuotaInfo returns a newly constructed QuotaInfo. 146 func NewQuotaInfo() *QuotaInfo { 147 return &QuotaInfo{ 148 Folders: make(map[string]*UsageStat), 149 Total: NewUsageStat(), 150 } 151 } 152 153 // QuotaInfoFromProtocol converts the RPC format into the local format. 154 func QuotaInfoFromProtocol(info keybase1.BlockQuotaInfo) *QuotaInfo { 155 u := &QuotaInfo{ 156 Folders: make(map[string]*UsageStat, len(info.Folders)), 157 Total: UsageStatFromProtocol(info.Total), 158 Limit: info.Limit, 159 GitLimit: info.GitLimit, 160 } 161 for _, stat := range info.Folders { 162 u.Folders[stat.FolderID] = UsageStatFromProtocol(stat.Stats) 163 } 164 return u 165 } 166 167 // AccumOne combines one quota charge to the existing QuotaInfo 168 func (u *QuotaInfo) AccumOne(change int, folder string, usage UsageType) { 169 if _, ok := u.Folders[folder]; !ok { 170 u.Folders[folder] = NewUsageStat() 171 } 172 u.Folders[folder].AccumOne(change, usage) 173 u.Total.AccumOne(change, usage) 174 } 175 176 // Accum combines changes to the existing QuotaInfo object using accumulation function accumF. 177 func (u *QuotaInfo) Accum(another *QuotaInfo, accumF func(int64, int64) int64) { 178 if another == nil { 179 return 180 } 181 if u.Total == nil { 182 u.Total = NewUsageStat() 183 } 184 u.Total.Accum(another.Total, accumF) 185 for f, change := range another.Folders { 186 if _, ok := u.Folders[f]; !ok { 187 u.Folders[f] = NewUsageStat() 188 } 189 u.Folders[f].Accum(change, accumF) 190 } 191 } 192 193 // ToBytes marshals this QuotaInfo 194 func (u *QuotaInfo) ToBytes(codec kbfscodec.Codec) ([]byte, error) { 195 return codec.Encode(u) 196 } 197 198 // ToProtocol converts this info to the RPC protocol format. 199 func (u *QuotaInfo) ToProtocol() (res keybase1.BlockQuotaInfo) { 200 res.Limit = u.Limit 201 res.GitLimit = u.GitLimit 202 res.Total = u.Total.ToProtocol() 203 204 res.Folders = make([]keybase1.FolderUsageStat, 0, len(u.Folders)) 205 for f, us := range u.Folders { 206 res.Folders = append(res.Folders, keybase1.FolderUsageStat{ 207 FolderID: f, 208 Stats: us.ToProtocol(), 209 }) 210 } 211 212 return res 213 } 214 215 // QuotaInfoDecode decodes b into a QuotaInfo 216 func QuotaInfoDecode(b []byte, codec kbfscodec.Codec) ( 217 *QuotaInfo, error) { 218 var info QuotaInfo 219 err := codec.Decode(b, &info) 220 if err != nil { 221 return nil, err 222 } 223 224 return &info, nil 225 }