mirror of https://github.com/gofiber/fiber.git
v3: move most deps to go.mod
parent
f119794035
commit
8a15f77fc4
4
app.go
4
app.go
|
@ -30,9 +30,9 @@ import (
|
|||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gofiber/fiber/v3/internal/colorable"
|
||||
"github.com/gofiber/fiber/v3/internal/isatty"
|
||||
"github.com/gofiber/fiber/v3/utils"
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
|
|
4
ctx.go
4
ctx.go
|
@ -23,10 +23,10 @@ import (
|
|||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3/internal/bytebufferpool"
|
||||
"github.com/gofiber/fiber/v3/internal/dictpool"
|
||||
"github.com/gofiber/fiber/v3/internal/schema"
|
||||
"github.com/gofiber/fiber/v3/utils"
|
||||
"github.com/savsgio/dictpool"
|
||||
"github.com/valyala/bytebufferpool"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
|
|
|
@ -27,10 +27,10 @@ import (
|
|||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3/internal/bytebufferpool"
|
||||
"github.com/gofiber/fiber/v3/internal/storage/memory"
|
||||
"github.com/gofiber/fiber/v3/internal/template/html"
|
||||
"github.com/gofiber/fiber/v3/utils"
|
||||
"github.com/valyala/bytebufferpool"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
|
|
19
go.mod
19
go.mod
|
@ -3,13 +3,28 @@ module github.com/gofiber/fiber/v3
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/mattn/go-colorable v0.1.12
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d
|
||||
github.com/shirou/gopsutil/v3 v3.22.5
|
||||
github.com/tinylib/msgp v1.1.6
|
||||
github.com/valyala/bytebufferpool v1.0.0
|
||||
github.com/valyala/fasthttp v1.37.0
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9
|
||||
github.com/valyala/fasttemplate v1.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/klauspost/compress v1.15.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20220401102855-e56b59f40436 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
|
||||
)
|
||||
|
|
69
go.sum
69
go.sum
|
@ -1,24 +1,93 @@
|
|||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
|
||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
|
||||
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d h1:ICMDEgNgR5xFW6ZDeMKTtmh07YiLr7GkDw897I2DwKg=
|
||||
github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d/go.mod h1:jrsy/bTK2n5uybo7bAvtLGzmuzAbxp+nKS8bzgrZURE=
|
||||
github.com/savsgio/gotils v0.0.0-20220401102855-e56b59f40436 h1:sfTahD3f2BSjx9U3R4K09PkNuZZWthT7g6vzTIXNWkM=
|
||||
github.com/savsgio/gotils v0.0.0-20220401102855-e56b59f40436/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
|
||||
github.com/shirou/gopsutil/v3 v3.22.5 h1:atX36I/IXgFiB81687vSiBI5zrMsxcIBkP9cQMJQoJA=
|
||||
github.com/shirou/gopsutil/v3 v3.22.5/go.mod h1:so9G9VzeHt/hsd0YwqprnjHnfARAUktauykSbr+y2gA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
|
||||
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.37.0 h1:7WHCyI7EAkQMVmrfBhWTCOaeROb1aCBiTopx63LkMbE=
|
||||
github.com/valyala/fasthttp v1.37.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
|
||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -17,8 +17,8 @@ import (
|
|||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gofiber/fiber/v3/internal/bytebufferpool"
|
||||
"github.com/gofiber/fiber/v3/utils"
|
||||
"github.com/valyala/bytebufferpool"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3/internal/bytebufferpool"
|
||||
"github.com/gofiber/fiber/v3/utils"
|
||||
"github.com/valyala/bytebufferpool"
|
||||
)
|
||||
|
||||
var testSimpleHandler = func(c *Ctx) error {
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Aliaksandr Valialkin, VertaMedia
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
package bytebufferpool
|
||||
|
||||
import "io"
|
||||
|
||||
// ByteBuffer provides byte buffer, which can be used for minimizing
|
||||
// memory allocations.
|
||||
//
|
||||
// ByteBuffer may be used with functions appending data to the given []byte
|
||||
// slice. See example code for details.
|
||||
//
|
||||
// Use Get for obtaining an empty byte buffer.
|
||||
type ByteBuffer struct {
|
||||
|
||||
// B is a byte buffer to use in append-like workloads.
|
||||
// See example code for details.
|
||||
B []byte
|
||||
}
|
||||
|
||||
// Len returns the size of the byte buffer.
|
||||
func (b *ByteBuffer) Len() int {
|
||||
return len(b.B)
|
||||
}
|
||||
|
||||
// ReadFrom implements io.ReaderFrom.
|
||||
//
|
||||
// The function appends all the data read from r to b.
|
||||
func (b *ByteBuffer) ReadFrom(r io.Reader) (int64, error) {
|
||||
p := b.B
|
||||
nStart := int64(len(p))
|
||||
nMax := int64(cap(p))
|
||||
n := nStart
|
||||
if nMax == 0 {
|
||||
nMax = 64
|
||||
p = make([]byte, nMax)
|
||||
} else {
|
||||
p = p[:nMax]
|
||||
}
|
||||
for {
|
||||
if n == nMax {
|
||||
nMax *= 2
|
||||
bNew := make([]byte, nMax)
|
||||
copy(bNew, p)
|
||||
p = bNew
|
||||
}
|
||||
nn, err := r.Read(p[n:])
|
||||
n += int64(nn)
|
||||
if err != nil {
|
||||
b.B = p[:n]
|
||||
n -= nStart
|
||||
if err == io.EOF {
|
||||
return n, nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo implements io.WriterTo.
|
||||
func (b *ByteBuffer) WriteTo(w io.Writer) (int64, error) {
|
||||
n, err := w.Write(b.B)
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// Bytes returns b.B, i.e. all the bytes accumulated in the buffer.
|
||||
//
|
||||
// The purpose of this function is bytes.Buffer compatibility.
|
||||
func (b *ByteBuffer) Bytes() []byte {
|
||||
return b.B
|
||||
}
|
||||
|
||||
// Write implements io.Writer - it appends p to ByteBuffer.B
|
||||
func (b *ByteBuffer) Write(p []byte) (int, error) {
|
||||
b.B = append(b.B, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// WriteByte appends the byte c to the buffer.
|
||||
//
|
||||
// The purpose of this function is bytes.Buffer compatibility.
|
||||
//
|
||||
// The function always returns nil.
|
||||
func (b *ByteBuffer) WriteByte(c byte) error {
|
||||
b.B = append(b.B, c)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteString appends s to ByteBuffer.B.
|
||||
func (b *ByteBuffer) WriteString(s string) (int, error) {
|
||||
b.B = append(b.B, s...)
|
||||
return len(s), nil
|
||||
}
|
||||
|
||||
// Set sets ByteBuffer.B to p.
|
||||
func (b *ByteBuffer) Set(p []byte) {
|
||||
b.B = append(b.B[:0], p...)
|
||||
}
|
||||
|
||||
// SetString sets ByteBuffer.B to s.
|
||||
func (b *ByteBuffer) SetString(s string) {
|
||||
b.B = append(b.B[:0], s...)
|
||||
}
|
||||
|
||||
// String returns string representation of ByteBuffer.B.
|
||||
func (b *ByteBuffer) String() string {
|
||||
return string(b.B)
|
||||
}
|
||||
|
||||
// Reset makes ByteBuffer.B empty.
|
||||
func (b *ByteBuffer) Reset() {
|
||||
b.B = b.B[:0]
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
package bytebufferpool
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
minBitSize = 6 // 2**6=64 is a CPU cache line size
|
||||
steps = 20
|
||||
|
||||
minSize = 1 << minBitSize
|
||||
maxSize = 1 << (minBitSize + steps - 1)
|
||||
|
||||
calibrateCallsThreshold = 42000
|
||||
maxPercentile = 0.95
|
||||
)
|
||||
|
||||
// Pool represents byte buffer pool.
|
||||
//
|
||||
// Distinct pools may be used for distinct types of byte buffers.
|
||||
// Properly determined byte buffer types with their own pools may help reducing
|
||||
// memory waste.
|
||||
type Pool struct {
|
||||
calls [steps]uint64
|
||||
calibrating uint64
|
||||
|
||||
defaultSize uint64
|
||||
maxSize uint64
|
||||
|
||||
pool sync.Pool
|
||||
}
|
||||
|
||||
var defaultPool Pool
|
||||
|
||||
// Get returns an empty byte buffer from the pool.
|
||||
//
|
||||
// Got byte buffer may be returned to the pool via Put call.
|
||||
// This reduces the number of memory allocations required for byte buffer
|
||||
// management.
|
||||
func Get() *ByteBuffer { return defaultPool.Get() }
|
||||
|
||||
// Get returns new byte buffer with zero length.
|
||||
//
|
||||
// The byte buffer may be returned to the pool via Put after the use
|
||||
// in order to minimize GC overhead.
|
||||
func (p *Pool) Get() *ByteBuffer {
|
||||
v := p.pool.Get()
|
||||
if v != nil {
|
||||
return v.(*ByteBuffer)
|
||||
}
|
||||
return &ByteBuffer{
|
||||
B: make([]byte, 0, atomic.LoadUint64(&p.defaultSize)),
|
||||
}
|
||||
}
|
||||
|
||||
// Put returns byte buffer to the pool.
|
||||
//
|
||||
// ByteBuffer.B mustn't be touched after returning it to the pool.
|
||||
// Otherwise data races will occur.
|
||||
func Put(b *ByteBuffer) { defaultPool.Put(b) }
|
||||
|
||||
// Put releases byte buffer obtained via Get to the pool.
|
||||
//
|
||||
// The buffer mustn't be accessed after returning to the pool.
|
||||
func (p *Pool) Put(b *ByteBuffer) {
|
||||
idx := index(len(b.B))
|
||||
|
||||
if atomic.AddUint64(&p.calls[idx], 1) > calibrateCallsThreshold {
|
||||
p.calibrate()
|
||||
}
|
||||
|
||||
maxSize := int(atomic.LoadUint64(&p.maxSize))
|
||||
if maxSize == 0 || cap(b.B) <= maxSize {
|
||||
b.Reset()
|
||||
p.pool.Put(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pool) calibrate() {
|
||||
if !atomic.CompareAndSwapUint64(&p.calibrating, 0, 1) {
|
||||
return
|
||||
}
|
||||
|
||||
a := make(callSizes, 0, steps)
|
||||
var callsSum uint64
|
||||
for i := uint64(0); i < steps; i++ {
|
||||
calls := atomic.SwapUint64(&p.calls[i], 0)
|
||||
callsSum += calls
|
||||
a = append(a, callSize{
|
||||
calls: calls,
|
||||
size: minSize << i,
|
||||
})
|
||||
}
|
||||
sort.Sort(a)
|
||||
|
||||
defaultSize := a[0].size
|
||||
maxSize := defaultSize
|
||||
|
||||
maxSum := uint64(float64(callsSum) * maxPercentile)
|
||||
callsSum = 0
|
||||
for i := 0; i < steps; i++ {
|
||||
if callsSum > maxSum {
|
||||
break
|
||||
}
|
||||
callsSum += a[i].calls
|
||||
size := a[i].size
|
||||
if size > maxSize {
|
||||
maxSize = size
|
||||
}
|
||||
}
|
||||
|
||||
atomic.StoreUint64(&p.defaultSize, defaultSize)
|
||||
atomic.StoreUint64(&p.maxSize, maxSize)
|
||||
|
||||
atomic.StoreUint64(&p.calibrating, 0)
|
||||
}
|
||||
|
||||
type callSize struct {
|
||||
calls uint64
|
||||
size uint64
|
||||
}
|
||||
|
||||
type callSizes []callSize
|
||||
|
||||
func (ci callSizes) Len() int {
|
||||
return len(ci)
|
||||
}
|
||||
|
||||
func (ci callSizes) Less(i, j int) bool {
|
||||
return ci[i].calls > ci[j].calls
|
||||
}
|
||||
|
||||
func (ci callSizes) Swap(i, j int) {
|
||||
ci[i], ci[j] = ci[j], ci[i]
|
||||
}
|
||||
|
||||
func index(n int) int {
|
||||
n--
|
||||
n >>= minBitSize
|
||||
idx := 0
|
||||
for n > 0 {
|
||||
n >>= 1
|
||||
idx++
|
||||
}
|
||||
if idx >= steps {
|
||||
idx = steps - 1
|
||||
}
|
||||
return idx
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Yasuhiro Matsumoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,38 +0,0 @@
|
|||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
_ "github.com/gofiber/fiber/v3/internal/isatty"
|
||||
)
|
||||
|
||||
// NewColorable returns new instance of Writer which handles escape sequence.
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
panic("nil passed instead of *os.File to NewColorable()")
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
|
||||
func NewColorableStdout() io.Writer {
|
||||
return os.Stdout
|
||||
}
|
||||
|
||||
// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
|
||||
func NewColorableStderr() io.Writer {
|
||||
return os.Stderr
|
||||
}
|
||||
|
||||
// EnableColorsStdout enable colors if possible.
|
||||
func EnableColorsStdout(enabled *bool) func() {
|
||||
if enabled != nil {
|
||||
*enabled = true
|
||||
}
|
||||
return func() {}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
//go:build !windows && !appengine
|
||||
// +build !windows,!appengine
|
||||
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
_ "github.com/gofiber/fiber/v3/internal/isatty"
|
||||
)
|
||||
|
||||
// NewColorable returns new instance of Writer which handles escape sequence.
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
panic("nil passed instead of *os.File to NewColorable()")
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
|
||||
func NewColorableStdout() io.Writer {
|
||||
return os.Stdout
|
||||
}
|
||||
|
||||
// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
|
||||
func NewColorableStderr() io.Writer {
|
||||
return os.Stderr
|
||||
}
|
||||
|
||||
// EnableColorsStdout enable colors if possible.
|
||||
func EnableColorsStdout(enabled *bool) func() {
|
||||
if enabled != nil {
|
||||
*enabled = true
|
||||
}
|
||||
return func() {}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,55 +0,0 @@
|
|||
package colorable
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// NonColorable holds writer but removes escape sequence.
|
||||
type NonColorable struct {
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
// NewNonColorable returns new instance of Writer which removes escape sequence from Writer.
|
||||
func NewNonColorable(w io.Writer) io.Writer {
|
||||
return &NonColorable{out: w}
|
||||
}
|
||||
|
||||
// Write writes data on console
|
||||
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
||||
er := bytes.NewReader(data)
|
||||
var bw [1]byte
|
||||
loop:
|
||||
for {
|
||||
c1, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if c1 != 0x1b {
|
||||
bw[0] = c1
|
||||
w.out.Write(bw[:])
|
||||
continue
|
||||
}
|
||||
c2, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if c2 != 0x5b {
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for {
|
||||
c, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||
break
|
||||
}
|
||||
buf.Write([]byte(string(c)))
|
||||
}
|
||||
}
|
||||
|
||||
return len(data), nil
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2020-present Sergio Andres Virviescas Santana
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,164 +0,0 @@
|
|||
package dictpool
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/gofiber/fiber/v3/utils"
|
||||
)
|
||||
|
||||
func (d *Dict) allocKV() *KV {
|
||||
n := len(d.D)
|
||||
|
||||
if cap(d.D) > n {
|
||||
d.D = d.D[:n+1]
|
||||
} else {
|
||||
d.D = append(d.D, KV{})
|
||||
}
|
||||
|
||||
return &d.D[n]
|
||||
}
|
||||
|
||||
func (d *Dict) append(key string, value interface{}) {
|
||||
kv := d.allocKV()
|
||||
kv.Key = key
|
||||
kv.Value = value
|
||||
}
|
||||
|
||||
func (d *Dict) indexOf(key string) int {
|
||||
n := len(d.D)
|
||||
|
||||
if d.BinarySearch {
|
||||
idx := sort.Search(n, func(i int) bool {
|
||||
return key <= d.D[i].Key
|
||||
})
|
||||
|
||||
if idx < n && d.D[idx].Key == key {
|
||||
return idx
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < n; i++ {
|
||||
if d.D[i].Key == key {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// Len is the number of elements in the Dict.
|
||||
func (d *Dict) Len() int {
|
||||
return len(d.D)
|
||||
}
|
||||
|
||||
// Swap swaps the elements with indexes i and j.
|
||||
func (d *Dict) Swap(i, j int) {
|
||||
iKey, iValue := d.D[i].Key, d.D[i].Value
|
||||
jKey, jValue := d.D[j].Key, d.D[j].Value
|
||||
|
||||
d.D[i].Key, d.D[i].Value = jKey, jValue
|
||||
d.D[j].Key, d.D[j].Value = iKey, iValue
|
||||
}
|
||||
|
||||
// Less reports whether the element with
|
||||
// index i should sort before the element with index j.
|
||||
func (d *Dict) Less(i, j int) bool {
|
||||
return d.D[i].Key < d.D[j].Key
|
||||
}
|
||||
|
||||
// Get get data from key.
|
||||
func (d *Dict) Get(key string) interface{} {
|
||||
idx := d.indexOf(key)
|
||||
if idx > -1 {
|
||||
return d.D[idx].Value
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBytes get data from key.
|
||||
func (d *Dict) GetBytes(key []byte) interface{} {
|
||||
return d.Get(utils.UnsafeString(key))
|
||||
}
|
||||
|
||||
// Set set new key.
|
||||
func (d *Dict) Set(key string, value interface{}) {
|
||||
idx := d.indexOf(key)
|
||||
if idx > -1 {
|
||||
kv := &d.D[idx]
|
||||
kv.Value = value
|
||||
} else {
|
||||
d.append(key, value)
|
||||
|
||||
if d.BinarySearch {
|
||||
sort.Sort(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetBytes set new key.
|
||||
func (d *Dict) SetBytes(key []byte, value interface{}) {
|
||||
d.Set(utils.UnsafeString(key), value)
|
||||
}
|
||||
|
||||
// Del delete key.
|
||||
func (d *Dict) Del(key string) {
|
||||
idx := d.indexOf(key)
|
||||
if idx > -1 {
|
||||
n := len(d.D) - 1
|
||||
d.Swap(idx, n)
|
||||
d.D = d.D[:n] // Remove last position
|
||||
}
|
||||
}
|
||||
|
||||
// DelBytes delete key.
|
||||
func (d *Dict) DelBytes(key []byte) {
|
||||
d.Del(utils.UnsafeString(key))
|
||||
}
|
||||
|
||||
// Has check if key exists.
|
||||
func (d *Dict) Has(key string) bool {
|
||||
return d.indexOf(key) > -1
|
||||
}
|
||||
|
||||
// HasBytes check if key exists.
|
||||
func (d *Dict) HasBytes(key []byte) bool {
|
||||
return d.Has(utils.UnsafeString(key))
|
||||
}
|
||||
|
||||
// Reset reset dict.
|
||||
func (d *Dict) Reset() {
|
||||
d.D = d.D[:0]
|
||||
}
|
||||
|
||||
// Map convert to map.
|
||||
func (d *Dict) Map(dst DictMap) {
|
||||
for i := range d.D {
|
||||
kv := &d.D[i]
|
||||
|
||||
sd, ok := kv.Value.(*Dict)
|
||||
if ok {
|
||||
subDst := make(DictMap)
|
||||
sd.Map(subDst)
|
||||
dst[kv.Key] = subDst
|
||||
} else {
|
||||
dst[kv.Key] = kv.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse convert map to Dict.
|
||||
func (d *Dict) Parse(src DictMap) {
|
||||
d.Reset()
|
||||
|
||||
for k, v := range src {
|
||||
sv, ok := v.(map[string]interface{})
|
||||
if ok {
|
||||
subDict := new(Dict)
|
||||
subDict.Parse(sv)
|
||||
d.append(k, subDict)
|
||||
} else {
|
||||
d.append(k, v)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package dictpool
|
||||
|
||||
import "sync"
|
||||
|
||||
var defaultPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(Dict)
|
||||
},
|
||||
}
|
||||
|
||||
// AcquireDict acquire new dict.
|
||||
func AcquireDict() *Dict {
|
||||
return defaultPool.Get().(*Dict) // nolint:forcetypeassert
|
||||
}
|
||||
|
||||
// ReleaseDict release dict.
|
||||
func ReleaseDict(d *Dict) {
|
||||
d.Reset()
|
||||
defaultPool.Put(d)
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package dictpool
|
||||
|
||||
//go:generate msgp
|
||||
|
||||
// KV struct so it storages key/value data.
|
||||
type KV struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Dict dictionary as slice with better performance.
|
||||
type Dict struct {
|
||||
// D slice of KV for storage the data
|
||||
D []KV
|
||||
|
||||
// Use binary search to the get an item.
|
||||
// It's only useful on big heaps.
|
||||
//
|
||||
// WARNING: Increase searching performance on big heaps,
|
||||
// but whe set new items could be slowier due to the sorting.
|
||||
BinarySearch bool
|
||||
}
|
||||
|
||||
// DictMap dictionary as map.
|
||||
type DictMap map[string]interface{}
|
|
@ -1,509 +0,0 @@
|
|||
package dictpool
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v3/internal/msgp"
|
||||
)
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *Dict) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "D":
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D")
|
||||
return
|
||||
}
|
||||
if cap(z.D) >= int(zb0002) {
|
||||
z.D = (z.D)[:zb0002]
|
||||
} else {
|
||||
z.D = make([]KV, zb0002)
|
||||
}
|
||||
for za0001 := range z.D {
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001)
|
||||
return
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
zb0003--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Key":
|
||||
z.D[za0001].Key, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001, "Key")
|
||||
return
|
||||
}
|
||||
case "Value":
|
||||
z.D[za0001].Value, err = dc.ReadIntf()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001, "Value")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "BinarySearch":
|
||||
z.BinarySearch, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BinarySearch")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *Dict) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 2
|
||||
// write "D"
|
||||
err = en.Append(0x82, 0xa1, 0x44)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteArrayHeader(uint32(len(z.D)))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D")
|
||||
return
|
||||
}
|
||||
for za0001 := range z.D {
|
||||
// map header, size 2
|
||||
// write "Key"
|
||||
err = en.Append(0x82, 0xa3, 0x4b, 0x65, 0x79)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.D[za0001].Key)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001, "Key")
|
||||
return
|
||||
}
|
||||
// write "Value"
|
||||
err = en.Append(0xa5, 0x56, 0x61, 0x6c, 0x75, 0x65)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteIntf(z.D[za0001].Value)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001, "Value")
|
||||
return
|
||||
}
|
||||
}
|
||||
// write "BinarySearch"
|
||||
err = en.Append(0xac, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.BinarySearch)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BinarySearch")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *Dict) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 2
|
||||
// string "D"
|
||||
o = append(o, 0x82, 0xa1, 0x44)
|
||||
o = msgp.AppendArrayHeader(o, uint32(len(z.D)))
|
||||
for za0001 := range z.D {
|
||||
// map header, size 2
|
||||
// string "Key"
|
||||
o = append(o, 0x82, 0xa3, 0x4b, 0x65, 0x79)
|
||||
o = msgp.AppendString(o, z.D[za0001].Key)
|
||||
// string "Value"
|
||||
o = append(o, 0xa5, 0x56, 0x61, 0x6c, 0x75, 0x65)
|
||||
o, err = msgp.AppendIntf(o, z.D[za0001].Value)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001, "Value")
|
||||
return
|
||||
}
|
||||
}
|
||||
// string "BinarySearch"
|
||||
o = append(o, 0xac, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68)
|
||||
o = msgp.AppendBool(o, z.BinarySearch)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *Dict) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "D":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D")
|
||||
return
|
||||
}
|
||||
if cap(z.D) >= int(zb0002) {
|
||||
z.D = (z.D)[:zb0002]
|
||||
} else {
|
||||
z.D = make([]KV, zb0002)
|
||||
}
|
||||
for za0001 := range z.D {
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001)
|
||||
return
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
zb0003--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Key":
|
||||
z.D[za0001].Key, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001, "Key")
|
||||
return
|
||||
}
|
||||
case "Value":
|
||||
z.D[za0001].Value, bts, err = msgp.ReadIntfBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001, "Value")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "D", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "BinarySearch":
|
||||
z.BinarySearch, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BinarySearch")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *Dict) Msgsize() (s int) {
|
||||
s = 1 + 2 + msgp.ArrayHeaderSize
|
||||
for za0001 := range z.D {
|
||||
s += 1 + 4 + msgp.StringPrefixSize + len(z.D[za0001].Key) + 6 + msgp.GuessSize(z.D[za0001].Value)
|
||||
}
|
||||
s += 13 + msgp.BoolSize
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *DictMap) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if (*z) == nil {
|
||||
(*z) = make(DictMap, zb0003)
|
||||
} else if len((*z)) > 0 {
|
||||
for key := range *z {
|
||||
delete((*z), key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
zb0003--
|
||||
var zb0001 string
|
||||
var zb0002 interface{}
|
||||
zb0001, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
zb0002, err = dc.ReadIntf()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0001)
|
||||
return
|
||||
}
|
||||
(*z)[zb0001] = zb0002
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z DictMap) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = en.WriteMapHeader(uint32(len(z)))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0004, zb0005 := range z {
|
||||
err = en.WriteString(zb0004)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
err = en.WriteIntf(zb0005)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0004)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z DictMap) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
o = msgp.AppendMapHeader(o, uint32(len(z)))
|
||||
for zb0004, zb0005 := range z {
|
||||
o = msgp.AppendString(o, zb0004)
|
||||
o, err = msgp.AppendIntf(o, zb0005)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0004)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *DictMap) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if (*z) == nil {
|
||||
(*z) = make(DictMap, zb0003)
|
||||
} else if len((*z)) > 0 {
|
||||
for key := range *z {
|
||||
delete((*z), key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
var zb0001 string
|
||||
var zb0002 interface{}
|
||||
zb0003--
|
||||
zb0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
zb0002, bts, err = msgp.ReadIntfBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0001)
|
||||
return
|
||||
}
|
||||
(*z)[zb0001] = zb0002
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z DictMap) Msgsize() (s int) {
|
||||
s = msgp.MapHeaderSize
|
||||
if z != nil {
|
||||
for zb0004, zb0005 := range z {
|
||||
_ = zb0005
|
||||
s += msgp.StringPrefixSize + len(zb0004) + msgp.GuessSize(zb0005)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *KV) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Key":
|
||||
z.Key, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Key")
|
||||
return
|
||||
}
|
||||
case "Value":
|
||||
z.Value, err = dc.ReadIntf()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Value")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z KV) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 2
|
||||
// write "Key"
|
||||
err = en.Append(0x82, 0xa3, 0x4b, 0x65, 0x79)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Key)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Key")
|
||||
return
|
||||
}
|
||||
// write "Value"
|
||||
err = en.Append(0xa5, 0x56, 0x61, 0x6c, 0x75, 0x65)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteIntf(z.Value)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Value")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z KV) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 2
|
||||
// string "Key"
|
||||
o = append(o, 0x82, 0xa3, 0x4b, 0x65, 0x79)
|
||||
o = msgp.AppendString(o, z.Key)
|
||||
// string "Value"
|
||||
o = append(o, 0xa5, 0x56, 0x61, 0x6c, 0x75, 0x65)
|
||||
o, err = msgp.AppendIntf(o, z.Value)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Value")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *KV) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Key":
|
||||
z.Key, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Key")
|
||||
return
|
||||
}
|
||||
case "Value":
|
||||
z.Value, bts, err = msgp.ReadIntfBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Value")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z KV) Msgsize() (s int) {
|
||||
s = 1 + 4 + msgp.StringPrefixSize + len(z.Key) + 6 + msgp.GuessSize(z.Value)
|
||||
return
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Aliaksandr Valialkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -1,437 +0,0 @@
|
|||
// Package fasttemplate implements simple and fast template library.
|
||||
//
|
||||
// Fasttemplate is faster than text/template, strings.Replace
|
||||
// and strings.Replacer.
|
||||
//
|
||||
// Fasttemplate ideally fits for fast and simple placeholders' substitutions.
|
||||
package fasttemplate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/gofiber/fiber/v3/internal/bytebufferpool"
|
||||
)
|
||||
|
||||
// ExecuteFunc calls f on each template tag (placeholder) occurrence.
|
||||
//
|
||||
// Returns the number of bytes written to w.
|
||||
//
|
||||
// This function is optimized for constantly changing templates.
|
||||
// Use Template.ExecuteFunc for frozen templates.
|
||||
func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int64, error) {
|
||||
s := unsafeString2Bytes(template)
|
||||
a := unsafeString2Bytes(startTag)
|
||||
b := unsafeString2Bytes(endTag)
|
||||
|
||||
var nn int64
|
||||
var ni int
|
||||
var err error
|
||||
for {
|
||||
n := bytes.Index(s, a)
|
||||
if n < 0 {
|
||||
break
|
||||
}
|
||||
ni, err = w.Write(s[:n])
|
||||
nn += int64(ni)
|
||||
if err != nil {
|
||||
return nn, err
|
||||
}
|
||||
|
||||
s = s[n+len(a):]
|
||||
n = bytes.Index(s, b)
|
||||
if n < 0 {
|
||||
// cannot find end tag - just write it to the output.
|
||||
ni, _ = w.Write(a)
|
||||
nn += int64(ni)
|
||||
break
|
||||
}
|
||||
|
||||
ni, err = f(w, unsafeBytes2String(s[:n]))
|
||||
nn += int64(ni)
|
||||
if err != nil {
|
||||
return nn, err
|
||||
}
|
||||
s = s[n+len(b):]
|
||||
}
|
||||
ni, err = w.Write(s)
|
||||
nn += int64(ni)
|
||||
|
||||
return nn, err
|
||||
}
|
||||
|
||||
// Execute substitutes template tags (placeholders) with the corresponding
|
||||
// values from the map m and writes the result to the given writer w.
|
||||
//
|
||||
// Substitution map m may contain values with the following types:
|
||||
// * []byte - the fastest value type
|
||||
// * string - convenient value type
|
||||
// * TagFunc - flexible value type
|
||||
//
|
||||
// Returns the number of bytes written to w.
|
||||
//
|
||||
// This function is optimized for constantly changing templates.
|
||||
// Use Template.Execute for frozen templates.
|
||||
func Execute(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) {
|
||||
return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
||||
}
|
||||
|
||||
// ExecuteStd works the same way as Execute, but keeps the unknown placeholders.
|
||||
// This can be used as a drop-in replacement for strings.Replacer
|
||||
//
|
||||
// Substitution map m may contain values with the following types:
|
||||
// * []byte - the fastest value type
|
||||
// * string - convenient value type
|
||||
// * TagFunc - flexible value type
|
||||
//
|
||||
// Returns the number of bytes written to w.
|
||||
//
|
||||
// This function is optimized for constantly changing templates.
|
||||
// Use Template.ExecuteStd for frozen templates.
|
||||
func ExecuteStd(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) {
|
||||
return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) })
|
||||
}
|
||||
|
||||
// ExecuteFuncString calls f on each template tag (placeholder) occurrence
|
||||
// and substitutes it with the data written to TagFunc's w.
|
||||
//
|
||||
// Returns the resulting string.
|
||||
//
|
||||
// This function is optimized for constantly changing templates.
|
||||
// Use Template.ExecuteFuncString for frozen templates.
|
||||
func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {
|
||||
s, err := ExecuteFuncStringWithErr(template, startTag, endTag, f)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unexpected error: %s", err))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ExecuteFuncStringWithErr is nearly the same as ExecuteFuncString
|
||||
// but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString
|
||||
// it just returns an empty string and the error f returned
|
||||
func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) {
|
||||
tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag))
|
||||
if tagsCount == 0 {
|
||||
return template, nil
|
||||
}
|
||||
|
||||
bb := byteBufferPool.Get()
|
||||
if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil {
|
||||
bb.Reset()
|
||||
byteBufferPool.Put(bb)
|
||||
return "", err
|
||||
}
|
||||
s := string(bb.B)
|
||||
bb.Reset()
|
||||
byteBufferPool.Put(bb)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
var byteBufferPool bytebufferpool.Pool
|
||||
|
||||
// ExecuteString substitutes template tags (placeholders) with the corresponding
|
||||
// values from the map m and returns the result.
|
||||
//
|
||||
// Substitution map m may contain values with the following types:
|
||||
// * []byte - the fastest value type
|
||||
// * string - convenient value type
|
||||
// * TagFunc - flexible value type
|
||||
//
|
||||
// This function is optimized for constantly changing templates.
|
||||
// Use Template.ExecuteString for frozen templates.
|
||||
func ExecuteString(template, startTag, endTag string, m map[string]interface{}) string {
|
||||
return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
||||
}
|
||||
|
||||
// ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders.
|
||||
// This can be used as a drop-in replacement for strings.Replacer
|
||||
//
|
||||
// Substitution map m may contain values with the following types:
|
||||
// * []byte - the fastest value type
|
||||
// * string - convenient value type
|
||||
// * TagFunc - flexible value type
|
||||
//
|
||||
// This function is optimized for constantly changing templates.
|
||||
// Use Template.ExecuteStringStd for frozen templates.
|
||||
func ExecuteStringStd(template, startTag, endTag string, m map[string]interface{}) string {
|
||||
return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) })
|
||||
}
|
||||
|
||||
// Template implements simple template engine, which can be used for fast
|
||||
// tags' (aka placeholders) substitution.
|
||||
type Template struct {
|
||||
template string
|
||||
startTag string
|
||||
endTag string
|
||||
|
||||
texts [][]byte
|
||||
tags []string
|
||||
byteBufferPool bytebufferpool.Pool
|
||||
}
|
||||
|
||||
// New parses the given template using the given startTag and endTag
|
||||
// as tag start and tag end.
|
||||
//
|
||||
// The returned template can be executed by concurrently running goroutines
|
||||
// using Execute* methods.
|
||||
//
|
||||
// New panics if the given template cannot be parsed. Use NewTemplate instead
|
||||
// if template may contain errors.
|
||||
func New(template, startTag, endTag string) *Template {
|
||||
t, err := NewTemplate(template, startTag, endTag)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// NewTemplate parses the given template using the given startTag and endTag
|
||||
// as tag start and tag end.
|
||||
//
|
||||
// The returned template can be executed by concurrently running goroutines
|
||||
// using Execute* methods.
|
||||
func NewTemplate(template, startTag, endTag string) (*Template, error) {
|
||||
var t Template
|
||||
err := t.Reset(template, startTag, endTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
// TagFunc can be used as a substitution value in the map passed to Execute*.
|
||||
// Execute* functions pass tag (placeholder) name in 'tag' argument.
|
||||
//
|
||||
// TagFunc must be safe to call from concurrently running goroutines.
|
||||
//
|
||||
// TagFunc must write contents to w and return the number of bytes written.
|
||||
type TagFunc func(w io.Writer, tag string) (int, error)
|
||||
|
||||
// Reset resets the template t to new one defined by
|
||||
// template, startTag and endTag.
|
||||
//
|
||||
// Reset allows Template object re-use.
|
||||
//
|
||||
// Reset may be called only if no other goroutines call t methods at the moment.
|
||||
func (t *Template) Reset(template, startTag, endTag string) error {
|
||||
// Keep these vars in t, so GC won't collect them and won't break
|
||||
// vars derived via unsafe*
|
||||
t.template = template
|
||||
t.startTag = startTag
|
||||
t.endTag = endTag
|
||||
t.texts = t.texts[:0]
|
||||
t.tags = t.tags[:0]
|
||||
|
||||
if len(startTag) == 0 {
|
||||
panic("startTag cannot be empty")
|
||||
}
|
||||
if len(endTag) == 0 {
|
||||
panic("endTag cannot be empty")
|
||||
}
|
||||
|
||||
s := unsafeString2Bytes(template)
|
||||
a := unsafeString2Bytes(startTag)
|
||||
b := unsafeString2Bytes(endTag)
|
||||
|
||||
tagsCount := bytes.Count(s, a)
|
||||
if tagsCount == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if tagsCount+1 > cap(t.texts) {
|
||||
t.texts = make([][]byte, 0, tagsCount+1)
|
||||
}
|
||||
if tagsCount > cap(t.tags) {
|
||||
t.tags = make([]string, 0, tagsCount)
|
||||
}
|
||||
|
||||
for {
|
||||
n := bytes.Index(s, a)
|
||||
if n < 0 {
|
||||
t.texts = append(t.texts, s)
|
||||
break
|
||||
}
|
||||
t.texts = append(t.texts, s[:n])
|
||||
|
||||
s = s[n+len(a):]
|
||||
n = bytes.Index(s, b)
|
||||
if n < 0 {
|
||||
return fmt.Errorf("cannot find end tag=%q in the template=%q starting from %q", endTag, template, s)
|
||||
}
|
||||
|
||||
t.tags = append(t.tags, unsafeBytes2String(s[:n]))
|
||||
s = s[n+len(b):]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecuteFunc calls f on each template tag (placeholder) occurrence.
|
||||
//
|
||||
// Returns the number of bytes written to w.
|
||||
//
|
||||
// This function is optimized for frozen templates.
|
||||
// Use ExecuteFunc for constantly changing templates.
|
||||
func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) {
|
||||
var nn int64
|
||||
|
||||
n := len(t.texts) - 1
|
||||
if n == -1 {
|
||||
ni, err := w.Write(unsafeString2Bytes(t.template))
|
||||
return int64(ni), err
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
ni, err := w.Write(t.texts[i])
|
||||
nn += int64(ni)
|
||||
if err != nil {
|
||||
return nn, err
|
||||
}
|
||||
|
||||
ni, err = f(w, t.tags[i])
|
||||
nn += int64(ni)
|
||||
if err != nil {
|
||||
return nn, err
|
||||
}
|
||||
}
|
||||
ni, err := w.Write(t.texts[n])
|
||||
nn += int64(ni)
|
||||
return nn, err
|
||||
}
|
||||
|
||||
// Execute substitutes template tags (placeholders) with the corresponding
|
||||
// values from the map m and writes the result to the given writer w.
|
||||
//
|
||||
// Substitution map m may contain values with the following types:
|
||||
// * []byte - the fastest value type
|
||||
// * string - convenient value type
|
||||
// * TagFunc - flexible value type
|
||||
//
|
||||
// Returns the number of bytes written to w.
|
||||
func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) {
|
||||
return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
||||
}
|
||||
|
||||
// ExecuteStd works the same way as Execute, but keeps the unknown placeholders.
|
||||
// This can be used as a drop-in replacement for strings.Replacer
|
||||
//
|
||||
// Substitution map m may contain values with the following types:
|
||||
// * []byte - the fastest value type
|
||||
// * string - convenient value type
|
||||
// * TagFunc - flexible value type
|
||||
//
|
||||
// Returns the number of bytes written to w.
|
||||
func (t *Template) ExecuteStd(w io.Writer, m map[string]interface{}) (int64, error) {
|
||||
return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) })
|
||||
}
|
||||
|
||||
// ExecuteFuncString calls f on each template tag (placeholder) occurrence
|
||||
// and substitutes it with the data written to TagFunc's w.
|
||||
//
|
||||
// Returns the resulting string.
|
||||
//
|
||||
// This function is optimized for frozen templates.
|
||||
// Use ExecuteFuncString for constantly changing templates.
|
||||
func (t *Template) ExecuteFuncString(f TagFunc) string {
|
||||
s, err := t.ExecuteFuncStringWithErr(f)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unexpected error: %s", err))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ExecuteFuncStringWithErr calls f on each template tag (placeholder) occurrence
|
||||
// and substitutes it with the data written to TagFunc's w.
|
||||
//
|
||||
// Returns the resulting string.
|
||||
//
|
||||
// This function is optimized for frozen templates.
|
||||
// Use ExecuteFuncString for constantly changing templates.
|
||||
func (t *Template) ExecuteFuncStringWithErr(f TagFunc) (string, error) {
|
||||
bb := t.byteBufferPool.Get()
|
||||
if _, err := t.ExecuteFunc(bb, f); err != nil {
|
||||
bb.Reset()
|
||||
t.byteBufferPool.Put(bb)
|
||||
return "", err
|
||||
}
|
||||
s := string(bb.Bytes())
|
||||
bb.Reset()
|
||||
t.byteBufferPool.Put(bb)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// ExecuteString substitutes template tags (placeholders) with the corresponding
|
||||
// values from the map m and returns the result.
|
||||
//
|
||||
// Substitution map m may contain values with the following types:
|
||||
// * []byte - the fastest value type
|
||||
// * string - convenient value type
|
||||
// * TagFunc - flexible value type
|
||||
//
|
||||
// This function is optimized for frozen templates.
|
||||
// Use ExecuteString for constantly changing templates.
|
||||
func (t *Template) ExecuteString(m map[string]interface{}) string {
|
||||
return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
||||
}
|
||||
|
||||
// ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders.
|
||||
// This can be used as a drop-in replacement for strings.Replacer
|
||||
//
|
||||
// Substitution map m may contain values with the following types:
|
||||
// * []byte - the fastest value type
|
||||
// * string - convenient value type
|
||||
// * TagFunc - flexible value type
|
||||
//
|
||||
// This function is optimized for frozen templates.
|
||||
// Use ExecuteStringStd for constantly changing templates.
|
||||
func (t *Template) ExecuteStringStd(m map[string]interface{}) string {
|
||||
return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) })
|
||||
}
|
||||
|
||||
func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) {
|
||||
v := m[tag]
|
||||
if v == nil {
|
||||
return 0, nil
|
||||
}
|
||||
switch value := v.(type) {
|
||||
case []byte:
|
||||
return w.Write(value)
|
||||
case string:
|
||||
return w.Write([]byte(value))
|
||||
case TagFunc:
|
||||
return value(w, tag)
|
||||
default:
|
||||
panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
|
||||
}
|
||||
}
|
||||
|
||||
func keepUnknownTagFunc(w io.Writer, startTag, endTag, tag string, m map[string]interface{}) (int, error) {
|
||||
v, ok := m[tag]
|
||||
if !ok {
|
||||
if _, err := w.Write(unsafeString2Bytes(startTag)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, err := w.Write(unsafeString2Bytes(tag)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, err := w.Write(unsafeString2Bytes(endTag)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(startTag) + len(tag) + len(endTag), nil
|
||||
}
|
||||
if v == nil {
|
||||
return 0, nil
|
||||
}
|
||||
switch value := v.(type) {
|
||||
case []byte:
|
||||
return w.Write(value)
|
||||
case string:
|
||||
return w.Write([]byte(value))
|
||||
case TagFunc:
|
||||
return value(w, tag)
|
||||
default:
|
||||
panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
//go:build !appengine
|
||||
// +build !appengine
|
||||
|
||||
package fasttemplate
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func unsafeBytes2String(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
func unsafeString2Bytes(s string) (b []byte) {
|
||||
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
bh.Data = sh.Data
|
||||
bh.Cap = sh.Len
|
||||
bh.Len = sh.Len
|
||||
return b
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
package fasttemplate
|
||||
|
||||
func unsafeBytes2String(b []byte) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func unsafeString2Bytes(s string) []byte {
|
||||
return []byte(s)
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
Copyright (c) 2014-2015, Philip Hofer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,396 +0,0 @@
|
|||
// The `fwd` package provides a buffered reader
|
||||
// and writer. Each has methods that help improve
|
||||
// the encoding/decoding performance of some binary
|
||||
// protocols.
|
||||
//
|
||||
// The `fwd.Writer` and `fwd.Reader` type provide similar
|
||||
// functionality to their counterparts in `bufio`, plus
|
||||
// a few extra utility methods that simplify read-ahead
|
||||
// and write-ahead. I wrote this package to improve serialization
|
||||
// performance for http://github.com/tinylib/msgp,
|
||||
// where it provided about a 2x speedup over `bufio` for certain
|
||||
// workloads. However, care must be taken to understand the semantics of the
|
||||
// extra methods provided by this package, as they allow
|
||||
// the user to access and manipulate the buffer memory
|
||||
// directly.
|
||||
//
|
||||
// The extra methods for `fwd.Reader` are `Peek`, `Skip`
|
||||
// and `Next`. `(*fwd.Reader).Peek`, unlike `(*bufio.Reader).Peek`,
|
||||
// will re-allocate the read buffer in order to accommodate arbitrarily
|
||||
// large read-ahead. `(*fwd.Reader).Skip` skips the next `n` bytes
|
||||
// in the stream, and uses the `io.Seeker` interface if the underlying
|
||||
// stream implements it. `(*fwd.Reader).Next` returns a slice pointing
|
||||
// to the next `n` bytes in the read buffer (like `Peek`), but also
|
||||
// increments the read position. This allows users to process streams
|
||||
// in arbitrary block sizes without having to manage appropriately-sized
|
||||
// slices. Additionally, obviating the need to copy the data from the
|
||||
// buffer to another location in memory can improve performance dramatically
|
||||
// in CPU-bound applications.
|
||||
//
|
||||
// `fwd.Writer` only has one extra method, which is `(*fwd.Writer).Next`, which
|
||||
// returns a slice pointing to the next `n` bytes of the writer, and increments
|
||||
// the write position by the length of the returned slice. This allows users
|
||||
// to write directly to the end of the buffer.
|
||||
//
|
||||
package fwd
|
||||
|
||||
import "io"
|
||||
|
||||
const (
|
||||
// DefaultReaderSize is the default size of the read buffer
|
||||
DefaultReaderSize = 2048
|
||||
|
||||
// minimum read buffer; straight from bufio
|
||||
minReaderSize = 16
|
||||
)
|
||||
|
||||
// NewReader returns a new *Reader that reads from 'r'
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return NewReaderSize(r, DefaultReaderSize)
|
||||
}
|
||||
|
||||
// NewReaderSize returns a new *Reader that
|
||||
// reads from 'r' and has a buffer size 'n'.
|
||||
func NewReaderSize(r io.Reader, n int) *Reader {
|
||||
buf := make([]byte, 0, max(n, minReaderSize))
|
||||
return NewReaderBuf(r, buf)
|
||||
}
|
||||
|
||||
// NewReaderBuf returns a new *Reader that
|
||||
// reads from 'r' and uses 'buf' as a buffer.
|
||||
// 'buf' is not used when has smaller capacity than 16,
|
||||
// custom buffer is allocated instead.
|
||||
func NewReaderBuf(r io.Reader, buf []byte) *Reader {
|
||||
if cap(buf) < minReaderSize {
|
||||
buf = make([]byte, 0, minReaderSize)
|
||||
}
|
||||
buf = buf[:0]
|
||||
rd := &Reader{
|
||||
r: r,
|
||||
data: buf,
|
||||
}
|
||||
if s, ok := r.(io.Seeker); ok {
|
||||
rd.rs = s
|
||||
}
|
||||
return rd
|
||||
}
|
||||
|
||||
// Reader is a buffered look-ahead reader
|
||||
type Reader struct {
|
||||
r io.Reader // underlying reader
|
||||
|
||||
// data[n:len(data)] is buffered data; data[len(data):cap(data)] is free buffer space
|
||||
data []byte // data
|
||||
n int // read offset
|
||||
state error // last read error
|
||||
|
||||
// if the reader past to NewReader was
|
||||
// also an io.Seeker, this is non-nil
|
||||
rs io.Seeker
|
||||
}
|
||||
|
||||
// Reset resets the underlying reader
|
||||
// and the read buffer.
|
||||
func (r *Reader) Reset(rd io.Reader) {
|
||||
r.r = rd
|
||||
r.data = r.data[0:0]
|
||||
r.n = 0
|
||||
r.state = nil
|
||||
if s, ok := rd.(io.Seeker); ok {
|
||||
r.rs = s
|
||||
} else {
|
||||
r.rs = nil
|
||||
}
|
||||
}
|
||||
|
||||
// more() does one read on the underlying reader
|
||||
func (r *Reader) more() {
|
||||
// move data backwards so that
|
||||
// the read offset is 0; this way
|
||||
// we can supply the maximum number of
|
||||
// bytes to the reader
|
||||
if r.n != 0 {
|
||||
if r.n < len(r.data) {
|
||||
r.data = r.data[:copy(r.data[0:], r.data[r.n:])]
|
||||
} else {
|
||||
r.data = r.data[:0]
|
||||
}
|
||||
r.n = 0
|
||||
}
|
||||
var a int
|
||||
a, r.state = r.r.Read(r.data[len(r.data):cap(r.data)])
|
||||
if a == 0 && r.state == nil {
|
||||
r.state = io.ErrNoProgress
|
||||
return
|
||||
} else if a > 0 && r.state == io.EOF {
|
||||
// discard the io.EOF if we read more than 0 bytes.
|
||||
// the next call to Read should return io.EOF again.
|
||||
r.state = nil
|
||||
}
|
||||
r.data = r.data[:len(r.data)+a]
|
||||
}
|
||||
|
||||
// pop error
|
||||
func (r *Reader) err() (e error) {
|
||||
e, r.state = r.state, nil
|
||||
return
|
||||
}
|
||||
|
||||
// pop error; EOF -> io.ErrUnexpectedEOF
|
||||
func (r *Reader) noEOF() (e error) {
|
||||
e, r.state = r.state, nil
|
||||
if e == io.EOF {
|
||||
e = io.ErrUnexpectedEOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// buffered bytes
|
||||
func (r *Reader) buffered() int { return len(r.data) - r.n }
|
||||
|
||||
// Buffered returns the number of bytes currently in the buffer
|
||||
func (r *Reader) Buffered() int { return len(r.data) - r.n }
|
||||
|
||||
// BufferSize returns the total size of the buffer
|
||||
func (r *Reader) BufferSize() int { return cap(r.data) }
|
||||
|
||||
// Peek returns the next 'n' buffered bytes,
|
||||
// reading from the underlying reader if necessary.
|
||||
// It will only return a slice shorter than 'n' bytes
|
||||
// if it also returns an error. Peek does not advance
|
||||
// the reader. EOF errors are *not* returned as
|
||||
// io.ErrUnexpectedEOF.
|
||||
func (r *Reader) Peek(n int) ([]byte, error) {
|
||||
// in the degenerate case,
|
||||
// we may need to realloc
|
||||
// (the caller asked for more
|
||||
// bytes than the size of the buffer)
|
||||
if cap(r.data) < n {
|
||||
old := r.data[r.n:]
|
||||
r.data = make([]byte, n+r.buffered())
|
||||
r.data = r.data[:copy(r.data, old)]
|
||||
r.n = 0
|
||||
}
|
||||
|
||||
// keep filling until
|
||||
// we hit an error or
|
||||
// read enough bytes
|
||||
for r.buffered() < n && r.state == nil {
|
||||
r.more()
|
||||
}
|
||||
|
||||
// we must have hit an error
|
||||
if r.buffered() < n {
|
||||
return r.data[r.n:], r.err()
|
||||
}
|
||||
|
||||
return r.data[r.n : r.n+n], nil
|
||||
}
|
||||
|
||||
// Skip moves the reader forward 'n' bytes.
|
||||
// Returns the number of bytes skipped and any
|
||||
// errors encountered. It is analogous to Seek(n, 1).
|
||||
// If the underlying reader implements io.Seeker, then
|
||||
// that method will be used to skip forward.
|
||||
//
|
||||
// If the reader encounters
|
||||
// an EOF before skipping 'n' bytes, it
|
||||
// returns io.ErrUnexpectedEOF. If the
|
||||
// underlying reader implements io.Seeker, then
|
||||
// those rules apply instead. (Many implementations
|
||||
// will not return `io.EOF` until the next call
|
||||
// to Read.)
|
||||
func (r *Reader) Skip(n int) (int, error) {
|
||||
|
||||
// fast path
|
||||
if r.buffered() >= n {
|
||||
r.n += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// use seeker implementation
|
||||
// if we can
|
||||
if r.rs != nil {
|
||||
return r.skipSeek(n)
|
||||
}
|
||||
|
||||
// loop on filling
|
||||
// and then erasing
|
||||
o := n
|
||||
for r.buffered() < n && r.state == nil {
|
||||
r.more()
|
||||
// we can skip forward
|
||||
// up to r.buffered() bytes
|
||||
step := min(r.buffered(), n)
|
||||
r.n += step
|
||||
n -= step
|
||||
}
|
||||
// at this point, n should be
|
||||
// 0 if everything went smoothly
|
||||
return o - n, r.noEOF()
|
||||
}
|
||||
|
||||
// Next returns the next 'n' bytes in the stream.
|
||||
// Unlike Peek, Next advances the reader position.
|
||||
// The returned bytes point to the same
|
||||
// data as the buffer, so the slice is
|
||||
// only valid until the next reader method call.
|
||||
// An EOF is considered an unexpected error.
|
||||
// If an the returned slice is less than the
|
||||
// length asked for, an error will be returned,
|
||||
// and the reader position will not be incremented.
|
||||
func (r *Reader) Next(n int) ([]byte, error) {
|
||||
|
||||
// in case the buffer is too small
|
||||
if cap(r.data) < n {
|
||||
old := r.data[r.n:]
|
||||
r.data = make([]byte, n+r.buffered())
|
||||
r.data = r.data[:copy(r.data, old)]
|
||||
r.n = 0
|
||||
}
|
||||
|
||||
// fill at least 'n' bytes
|
||||
for r.buffered() < n && r.state == nil {
|
||||
r.more()
|
||||
}
|
||||
|
||||
if r.buffered() < n {
|
||||
return r.data[r.n:], r.noEOF()
|
||||
}
|
||||
out := r.data[r.n : r.n+n]
|
||||
r.n += n
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// skipSeek uses the io.Seeker to seek forward.
|
||||
// only call this function when n > r.buffered()
|
||||
func (r *Reader) skipSeek(n int) (int, error) {
|
||||
o := r.buffered()
|
||||
// first, clear buffer
|
||||
n -= o
|
||||
r.n = 0
|
||||
r.data = r.data[:0]
|
||||
|
||||
// then seek forward remaning bytes
|
||||
i, err := r.rs.Seek(int64(n), 1)
|
||||
return int(i) + o, err
|
||||
}
|
||||
|
||||
// Read implements `io.Reader`
|
||||
func (r *Reader) Read(b []byte) (int, error) {
|
||||
// if we have data in the buffer, just
|
||||
// return that.
|
||||
if r.buffered() != 0 {
|
||||
x := copy(b, r.data[r.n:])
|
||||
r.n += x
|
||||
return x, nil
|
||||
}
|
||||
var n int
|
||||
// we have no buffered data; determine
|
||||
// whether or not to buffer or call
|
||||
// the underlying reader directly
|
||||
if len(b) >= cap(r.data) {
|
||||
n, r.state = r.r.Read(b)
|
||||
} else {
|
||||
r.more()
|
||||
n = copy(b, r.data)
|
||||
r.n = n
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, r.err()
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ReadFull attempts to read len(b) bytes into
|
||||
// 'b'. It returns the number of bytes read into
|
||||
// 'b', and an error if it does not return len(b).
|
||||
// EOF is considered an unexpected error.
|
||||
func (r *Reader) ReadFull(b []byte) (int, error) {
|
||||
var n int // read into b
|
||||
var nn int // scratch
|
||||
l := len(b)
|
||||
// either read buffered data,
|
||||
// or read directly for the underlying
|
||||
// buffer, or fetch more buffered data.
|
||||
for n < l && r.state == nil {
|
||||
if r.buffered() != 0 {
|
||||
nn = copy(b[n:], r.data[r.n:])
|
||||
n += nn
|
||||
r.n += nn
|
||||
} else if l-n > cap(r.data) {
|
||||
nn, r.state = r.r.Read(b[n:])
|
||||
n += nn
|
||||
} else {
|
||||
r.more()
|
||||
}
|
||||
}
|
||||
if n < l {
|
||||
return n, r.noEOF()
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ReadByte implements `io.ByteReader`
|
||||
func (r *Reader) ReadByte() (byte, error) {
|
||||
for r.buffered() < 1 && r.state == nil {
|
||||
r.more()
|
||||
}
|
||||
if r.buffered() < 1 {
|
||||
return 0, r.err()
|
||||
}
|
||||
b := r.data[r.n]
|
||||
r.n++
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// WriteTo implements `io.WriterTo`
|
||||
func (r *Reader) WriteTo(w io.Writer) (int64, error) {
|
||||
var (
|
||||
i int64
|
||||
ii int
|
||||
err error
|
||||
)
|
||||
// first, clear buffer
|
||||
if r.buffered() > 0 {
|
||||
ii, err = w.Write(r.data[r.n:])
|
||||
i += int64(ii)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
r.data = r.data[0:0]
|
||||
r.n = 0
|
||||
}
|
||||
for r.state == nil {
|
||||
// here we just do
|
||||
// 1:1 reads and writes
|
||||
r.more()
|
||||
if r.buffered() > 0 {
|
||||
ii, err = w.Write(r.data)
|
||||
i += int64(ii)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
r.data = r.data[0:0]
|
||||
r.n = 0
|
||||
}
|
||||
}
|
||||
if r.state != io.EOF {
|
||||
return i, r.err()
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func min(a int, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func max(a int, b int) int {
|
||||
if a < b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
package fwd
|
||||
|
||||
import "io"
|
||||
|
||||
const (
|
||||
// DefaultWriterSize is the
|
||||
// default write buffer size.
|
||||
DefaultWriterSize = 2048
|
||||
|
||||
minWriterSize = minReaderSize
|
||||
)
|
||||
|
||||
// Writer is a buffered writer
|
||||
type Writer struct {
|
||||
w io.Writer // writer
|
||||
buf []byte // 0:len(buf) is bufered data
|
||||
}
|
||||
|
||||
// NewWriter returns a new writer
|
||||
// that writes to 'w' and has a buffer
|
||||
// that is `DefaultWriterSize` bytes.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
if wr, ok := w.(*Writer); ok {
|
||||
return wr
|
||||
}
|
||||
return &Writer{
|
||||
w: w,
|
||||
buf: make([]byte, 0, DefaultWriterSize),
|
||||
}
|
||||
}
|
||||
|
||||
// NewWriterSize returns a new writer that
|
||||
// writes to 'w' and has a buffer size 'n'.
|
||||
func NewWriterSize(w io.Writer, n int) *Writer {
|
||||
if wr, ok := w.(*Writer); ok && cap(wr.buf) >= n {
|
||||
return wr
|
||||
}
|
||||
buf := make([]byte, 0, max(n, minWriterSize))
|
||||
return NewWriterBuf(w, buf)
|
||||
}
|
||||
|
||||
// NewWriterBuf returns a new writer
|
||||
// that writes to 'w' and has 'buf' as a buffer.
|
||||
// 'buf' is not used when has smaller capacity than 18,
|
||||
// custom buffer is allocated instead.
|
||||
func NewWriterBuf(w io.Writer, buf []byte) *Writer {
|
||||
if cap(buf) < minWriterSize {
|
||||
buf = make([]byte, 0, minWriterSize)
|
||||
}
|
||||
buf = buf[:0]
|
||||
return &Writer{
|
||||
w: w,
|
||||
buf: buf,
|
||||
}
|
||||
}
|
||||
|
||||
// Buffered returns the number of buffered bytes
|
||||
// in the reader.
|
||||
func (w *Writer) Buffered() int { return len(w.buf) }
|
||||
|
||||
// BufferSize returns the maximum size of the buffer.
|
||||
func (w *Writer) BufferSize() int { return cap(w.buf) }
|
||||
|
||||
// Flush flushes any buffered bytes
|
||||
// to the underlying writer.
|
||||
func (w *Writer) Flush() error {
|
||||
l := len(w.buf)
|
||||
if l > 0 {
|
||||
n, err := w.w.Write(w.buf)
|
||||
|
||||
// if we didn't write the whole
|
||||
// thing, copy the unwritten
|
||||
// bytes to the beginnning of the
|
||||
// buffer.
|
||||
if n < l && n > 0 {
|
||||
w.pushback(n)
|
||||
if err == nil {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.buf = w.buf[:0]
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write implements `io.Writer`
|
||||
func (w *Writer) Write(p []byte) (int, error) {
|
||||
c, l, ln := cap(w.buf), len(w.buf), len(p)
|
||||
avail := c - l
|
||||
|
||||
// requires flush
|
||||
if avail < ln {
|
||||
if err := w.Flush(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
l = len(w.buf)
|
||||
}
|
||||
// too big to fit in buffer;
|
||||
// write directly to w.w
|
||||
if c < ln {
|
||||
return w.w.Write(p)
|
||||
}
|
||||
|
||||
// grow buf slice; copy; return
|
||||
w.buf = w.buf[:l+ln]
|
||||
return copy(w.buf[l:], p), nil
|
||||
}
|
||||
|
||||
// WriteString is analogous to Write, but it takes a string.
|
||||
func (w *Writer) WriteString(s string) (int, error) {
|
||||
c, l, ln := cap(w.buf), len(w.buf), len(s)
|
||||
avail := c - l
|
||||
|
||||
// requires flush
|
||||
if avail < ln {
|
||||
if err := w.Flush(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
l = len(w.buf)
|
||||
}
|
||||
// too big to fit in buffer;
|
||||
// write directly to w.w
|
||||
//
|
||||
// yes, this is unsafe. *but*
|
||||
// io.Writer is not allowed
|
||||
// to mutate its input or
|
||||
// maintain a reference to it,
|
||||
// per the spec in package io.
|
||||
//
|
||||
// plus, if the string is really
|
||||
// too big to fit in the buffer, then
|
||||
// creating a copy to write it is
|
||||
// expensive (and, strictly speaking,
|
||||
// unnecessary)
|
||||
if c < ln {
|
||||
return w.w.Write(unsafestr(s))
|
||||
}
|
||||
|
||||
// grow buf slice; copy; return
|
||||
w.buf = w.buf[:l+ln]
|
||||
return copy(w.buf[l:], s), nil
|
||||
}
|
||||
|
||||
// WriteByte implements `io.ByteWriter`
|
||||
func (w *Writer) WriteByte(b byte) error {
|
||||
if len(w.buf) == cap(w.buf) {
|
||||
if err := w.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.buf = append(w.buf, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next returns the next 'n' free bytes
|
||||
// in the write buffer, flushing the writer
|
||||
// as necessary. Next will return `io.ErrShortBuffer`
|
||||
// if 'n' is greater than the size of the write buffer.
|
||||
// Calls to 'next' increment the write position by
|
||||
// the size of the returned buffer.
|
||||
func (w *Writer) Next(n int) ([]byte, error) {
|
||||
c, l := cap(w.buf), len(w.buf)
|
||||
if n > c {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
avail := c - l
|
||||
if avail < n {
|
||||
if err := w.Flush(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l = len(w.buf)
|
||||
}
|
||||
w.buf = w.buf[:l+n]
|
||||
return w.buf[l:], nil
|
||||
}
|
||||
|
||||
// take the bytes from w.buf[n:len(w.buf)]
|
||||
// and put them at the beginning of w.buf,
|
||||
// and resize to the length of the copied segment.
|
||||
func (w *Writer) pushback(n int) {
|
||||
w.buf = w.buf[:copy(w.buf, w.buf[n:])]
|
||||
}
|
||||
|
||||
// ReadFrom implements `io.ReaderFrom`
|
||||
func (w *Writer) ReadFrom(r io.Reader) (int64, error) {
|
||||
// anticipatory flush
|
||||
if err := w.Flush(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
w.buf = w.buf[0:cap(w.buf)] // expand buffer
|
||||
|
||||
var nn int64 // written
|
||||
var err error // error
|
||||
var x int // read
|
||||
|
||||
// 1:1 reads and writes
|
||||
for err == nil {
|
||||
x, err = r.Read(w.buf)
|
||||
if x > 0 {
|
||||
n, werr := w.w.Write(w.buf[:x])
|
||||
nn += int64(n)
|
||||
|
||||
if err != nil {
|
||||
if n < x && n > 0 {
|
||||
w.pushback(n - x)
|
||||
}
|
||||
return nn, werr
|
||||
}
|
||||
if n < x {
|
||||
w.pushback(n - x)
|
||||
return nn, io.ErrShortWrite
|
||||
}
|
||||
} else if err == nil {
|
||||
err = io.ErrNoProgress
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != io.EOF {
|
||||
return nn, err
|
||||
}
|
||||
|
||||
// we only clear here
|
||||
// because we are sure
|
||||
// the writes have
|
||||
// succeeded. otherwise,
|
||||
// we retain the data in case
|
||||
// future writes succeed.
|
||||
w.buf = w.buf[0:0]
|
||||
|
||||
return nn, nil
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
package fwd
|
||||
|
||||
func unsafestr(s string) []byte { return []byte(s) }
|
|
@ -1,19 +0,0 @@
|
|||
//go:build !appengine
|
||||
// +build !appengine
|
||||
|
||||
package fwd
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// unsafe cast string as []byte
|
||||
func unsafestr(b string) []byte {
|
||||
l := len(b)
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Len: l,
|
||||
Cap: l,
|
||||
Data: (*reflect.StringHeader)(unsafe.Pointer(&b)).Data,
|
||||
}))
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2013-2017 Yasuhiro Matsumoto, <mattn.jp@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,345 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
procCoInitialize = modole32.NewProc("CoInitialize")
|
||||
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
|
||||
procCoUninitialize = modole32.NewProc("CoUninitialize")
|
||||
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
|
||||
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
|
||||
procCLSIDFromProgID = modole32.NewProc("CLSIDFromProgID")
|
||||
procCLSIDFromString = modole32.NewProc("CLSIDFromString")
|
||||
procStringFromCLSID = modole32.NewProc("StringFromCLSID")
|
||||
procStringFromIID = modole32.NewProc("StringFromIID")
|
||||
procIIDFromString = modole32.NewProc("IIDFromString")
|
||||
procCoGetObject = modole32.NewProc("CoGetObject")
|
||||
procGetUserDefaultLCID = modkernel32.NewProc("GetUserDefaultLCID")
|
||||
procCopyMemory = modkernel32.NewProc("RtlMoveMemory")
|
||||
procVariantInit = modoleaut32.NewProc("VariantInit")
|
||||
procVariantClear = modoleaut32.NewProc("VariantClear")
|
||||
procVariantTimeToSystemTime = modoleaut32.NewProc("VariantTimeToSystemTime")
|
||||
procSysAllocString = modoleaut32.NewProc("SysAllocString")
|
||||
procSysAllocStringLen = modoleaut32.NewProc("SysAllocStringLen")
|
||||
procSysFreeString = modoleaut32.NewProc("SysFreeString")
|
||||
procSysStringLen = modoleaut32.NewProc("SysStringLen")
|
||||
procCreateDispTypeInfo = modoleaut32.NewProc("CreateDispTypeInfo")
|
||||
procCreateStdDispatch = modoleaut32.NewProc("CreateStdDispatch")
|
||||
procGetActiveObject = modoleaut32.NewProc("GetActiveObject")
|
||||
|
||||
procGetMessageW = moduser32.NewProc("GetMessageW")
|
||||
procDispatchMessageW = moduser32.NewProc("DispatchMessageW")
|
||||
)
|
||||
|
||||
// coInitialize initializes COM library on current thread.
|
||||
//
|
||||
// MSDN documentation suggests that this function should not be called. Call
|
||||
// CoInitializeEx() instead. The reason has to do with threading and this
|
||||
// function is only for single-threaded apartments.
|
||||
//
|
||||
// That said, most users of the library have gotten away with just this
|
||||
// function. If you are experiencing threading issues, then use
|
||||
// CoInitializeEx().
|
||||
func coInitialize() (err error) {
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx
|
||||
// Suggests that no value should be passed to CoInitialized.
|
||||
// Could just be Call() since the parameter is optional. <-- Needs testing to be sure.
|
||||
hr, _, _ := procCoInitialize.Call(uintptr(0))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// coInitializeEx initializes COM library with concurrency model.
|
||||
func coInitializeEx(coinit uint32) (err error) {
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx
|
||||
// Suggests that the first parameter is not only optional but should always be NULL.
|
||||
hr, _, _ := procCoInitializeEx.Call(uintptr(0), uintptr(coinit))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CoInitialize initializes COM library on current thread.
|
||||
//
|
||||
// MSDN documentation suggests that this function should not be called. Call
|
||||
// CoInitializeEx() instead. The reason has to do with threading and this
|
||||
// function is only for single-threaded apartments.
|
||||
//
|
||||
// That said, most users of the library have gotten away with just this
|
||||
// function. If you are experiencing threading issues, then use
|
||||
// CoInitializeEx().
|
||||
func CoInitialize(p uintptr) (err error) {
|
||||
// p is ignored and won't be used.
|
||||
// Avoid any variable not used errors.
|
||||
p = uintptr(0)
|
||||
return coInitialize()
|
||||
}
|
||||
|
||||
// CoInitializeEx initializes COM library with concurrency model.
|
||||
func CoInitializeEx(p uintptr, coinit uint32) (err error) {
|
||||
// Avoid any variable not used errors.
|
||||
p = uintptr(0)
|
||||
return coInitializeEx(coinit)
|
||||
}
|
||||
|
||||
// CoUninitialize uninitializes COM Library.
|
||||
func CoUninitialize() {
|
||||
procCoUninitialize.Call()
|
||||
}
|
||||
|
||||
// CoTaskMemFree frees memory pointer.
|
||||
func CoTaskMemFree(memptr uintptr) {
|
||||
procCoTaskMemFree.Call(memptr)
|
||||
}
|
||||
|
||||
// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
|
||||
//
|
||||
// The Programmatic Identifier must be registered, because it will be looked up
|
||||
// in the Windows Registry. The registry entry has the following keys: CLSID,
|
||||
// Insertable, Protocol and Shell
|
||||
// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
|
||||
//
|
||||
// programID identifies the class id with less precision and is not guaranteed
|
||||
// to be unique. These are usually found in the registry under
|
||||
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
|
||||
// "Program.Component.Version" with version being optional.
|
||||
//
|
||||
// CLSIDFromProgID in Windows API.
|
||||
func CLSIDFromProgID(progId string) (clsid *GUID, err error) {
|
||||
var guid GUID
|
||||
lpszProgID := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
|
||||
hr, _, _ := procCLSIDFromProgID.Call(lpszProgID, uintptr(unsafe.Pointer(&guid)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
clsid = &guid
|
||||
return
|
||||
}
|
||||
|
||||
// CLSIDFromString retrieves Class ID from string representation.
|
||||
//
|
||||
// This is technically the string version of the GUID and will convert the
|
||||
// string to object.
|
||||
//
|
||||
// CLSIDFromString in Windows API.
|
||||
func CLSIDFromString(str string) (clsid *GUID, err error) {
|
||||
var guid GUID
|
||||
lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))
|
||||
hr, _, _ := procCLSIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
clsid = &guid
|
||||
return
|
||||
}
|
||||
|
||||
// StringFromCLSID returns GUID formated string from GUID object.
|
||||
func StringFromCLSID(clsid *GUID) (str string, err error) {
|
||||
var p *uint16
|
||||
hr, _, _ := procStringFromCLSID.Call(uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(&p)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
str = LpOleStrToString(p)
|
||||
return
|
||||
}
|
||||
|
||||
// IIDFromString returns GUID from program ID.
|
||||
func IIDFromString(progId string) (clsid *GUID, err error) {
|
||||
var guid GUID
|
||||
lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
|
||||
hr, _, _ := procIIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
clsid = &guid
|
||||
return
|
||||
}
|
||||
|
||||
// StringFromIID returns GUID formatted string from GUID object.
|
||||
func StringFromIID(iid *GUID) (str string, err error) {
|
||||
var p *uint16
|
||||
hr, _, _ := procStringFromIID.Call(uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&p)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
str = LpOleStrToString(p)
|
||||
return
|
||||
}
|
||||
|
||||
// CreateInstance of single uninitialized object with GUID.
|
||||
func CreateInstance(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
|
||||
if iid == nil {
|
||||
iid = IID_IUnknown
|
||||
}
|
||||
hr, _, _ := procCoCreateInstance.Call(
|
||||
uintptr(unsafe.Pointer(clsid)),
|
||||
0,
|
||||
CLSCTX_SERVER,
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(&unk)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetActiveObject retrieves pointer to active object.
|
||||
func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
|
||||
if iid == nil {
|
||||
iid = IID_IUnknown
|
||||
}
|
||||
hr, _, _ := procGetActiveObject.Call(
|
||||
uintptr(unsafe.Pointer(clsid)),
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(&unk)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type BindOpts struct {
|
||||
CbStruct uint32
|
||||
GrfFlags uint32
|
||||
GrfMode uint32
|
||||
TickCountDeadline uint32
|
||||
}
|
||||
|
||||
// GetObject retrieves pointer to active object.
|
||||
func GetObject(programID string, bindOpts *BindOpts, iid *GUID) (unk *IUnknown, err error) {
|
||||
if bindOpts != nil {
|
||||
bindOpts.CbStruct = uint32(unsafe.Sizeof(BindOpts{}))
|
||||
}
|
||||
if iid == nil {
|
||||
iid = IID_IUnknown
|
||||
}
|
||||
hr, _, _ := procCoGetObject.Call(
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(programID))),
|
||||
uintptr(unsafe.Pointer(bindOpts)),
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(&unk)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// VariantInit initializes variant.
|
||||
func VariantInit(v *VARIANT) (err error) {
|
||||
hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// VariantClear clears value in Variant settings to VT_EMPTY.
|
||||
func VariantClear(v *VARIANT) (err error) {
|
||||
hr, _, _ := procVariantClear.Call(uintptr(unsafe.Pointer(v)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SysAllocString allocates memory for string and copies string into memory.
|
||||
func SysAllocString(v string) (ss *int16) {
|
||||
pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))
|
||||
ss = (*int16)(unsafe.Pointer(pss))
|
||||
return
|
||||
}
|
||||
|
||||
// SysAllocStringLen copies up to length of given string returning pointer.
|
||||
func SysAllocStringLen(v string) (ss *int16) {
|
||||
utf16 := utf16.Encode([]rune(v + "\x00"))
|
||||
ptr := &utf16[0]
|
||||
|
||||
pss, _, _ := procSysAllocStringLen.Call(uintptr(unsafe.Pointer(ptr)), uintptr(len(utf16)-1))
|
||||
ss = (*int16)(unsafe.Pointer(pss))
|
||||
return
|
||||
}
|
||||
|
||||
// SysFreeString frees string system memory. This must be called with SysAllocString.
|
||||
func SysFreeString(v *int16) (err error) {
|
||||
hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SysStringLen is the length of the system allocated string.
|
||||
func SysStringLen(v *int16) uint32 {
|
||||
l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v)))
|
||||
return uint32(l)
|
||||
}
|
||||
|
||||
// CreateStdDispatch provides default IDispatch implementation for IUnknown.
|
||||
//
|
||||
// This handles default IDispatch implementation for objects. It haves a few
|
||||
// limitations with only supporting one language. It will also only return
|
||||
// default exception codes.
|
||||
func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (disp *IDispatch, err error) {
|
||||
hr, _, _ := procCreateStdDispatch.Call(
|
||||
uintptr(unsafe.Pointer(unk)),
|
||||
v,
|
||||
uintptr(unsafe.Pointer(ptinfo)),
|
||||
uintptr(unsafe.Pointer(&disp)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
|
||||
//
|
||||
// This will not handle the full implementation of the interface.
|
||||
func CreateDispTypeInfo(idata *INTERFACEDATA) (pptinfo *IUnknown, err error) {
|
||||
hr, _, _ := procCreateDispTypeInfo.Call(
|
||||
uintptr(unsafe.Pointer(idata)),
|
||||
uintptr(GetUserDefaultLCID()),
|
||||
uintptr(unsafe.Pointer(&pptinfo)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// copyMemory moves location of a block of memory.
|
||||
func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {
|
||||
procCopyMemory.Call(uintptr(dest), uintptr(src), uintptr(length))
|
||||
}
|
||||
|
||||
// GetUserDefaultLCID retrieves current user default locale.
|
||||
func GetUserDefaultLCID() (lcid uint32) {
|
||||
ret, _, _ := procGetUserDefaultLCID.Call()
|
||||
lcid = uint32(ret)
|
||||
return
|
||||
}
|
||||
|
||||
// GetMessage in message queue from runtime.
|
||||
//
|
||||
// This function appears to block. PeekMessage does not block.
|
||||
func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) {
|
||||
r0, _, err := procGetMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax))
|
||||
ret = int32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
// DispatchMessage to window procedure.
|
||||
func DispatchMessage(msg *Msg) (ret int32) {
|
||||
r0, _, _ := procDispatchMessageW.Call(uintptr(unsafe.Pointer(msg)))
|
||||
ret = int32(r0)
|
||||
return
|
||||
}
|
|
@ -1,175 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// coInitialize initializes COM library on current thread.
|
||||
//
|
||||
// MSDN documentation suggests that this function should not be called. Call
|
||||
// CoInitializeEx() instead. The reason has to do with threading and this
|
||||
// function is only for single-threaded apartments.
|
||||
//
|
||||
// That said, most users of the library have gotten away with just this
|
||||
// function. If you are experiencing threading issues, then use
|
||||
// CoInitializeEx().
|
||||
func coInitialize() error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// coInitializeEx initializes COM library with concurrency model.
|
||||
func coInitializeEx(coinit uint32) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CoInitialize initializes COM library on current thread.
|
||||
//
|
||||
// MSDN documentation suggests that this function should not be called. Call
|
||||
// CoInitializeEx() instead. The reason has to do with threading and this
|
||||
// function is only for single-threaded apartments.
|
||||
//
|
||||
// That said, most users of the library have gotten away with just this
|
||||
// function. If you are experiencing threading issues, then use
|
||||
// CoInitializeEx().
|
||||
func CoInitialize(p uintptr) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CoInitializeEx initializes COM library with concurrency model.
|
||||
func CoInitializeEx(p uintptr, coinit uint32) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CoUninitialize uninitializes COM Library.
|
||||
func CoUninitialize() {}
|
||||
|
||||
// CoTaskMemFree frees memory pointer.
|
||||
func CoTaskMemFree(memptr uintptr) {}
|
||||
|
||||
// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
|
||||
//
|
||||
// The Programmatic Identifier must be registered, because it will be looked up
|
||||
// in the Windows Registry. The registry entry has the following keys: CLSID,
|
||||
// Insertable, Protocol and Shell
|
||||
// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
|
||||
//
|
||||
// programID identifies the class id with less precision and is not guaranteed
|
||||
// to be unique. These are usually found in the registry under
|
||||
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
|
||||
// "Program.Component.Version" with version being optional.
|
||||
//
|
||||
// CLSIDFromProgID in Windows API.
|
||||
func CLSIDFromProgID(progId string) (*GUID, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CLSIDFromString retrieves Class ID from string representation.
|
||||
//
|
||||
// This is technically the string version of the GUID and will convert the
|
||||
// string to object.
|
||||
//
|
||||
// CLSIDFromString in Windows API.
|
||||
func CLSIDFromString(str string) (*GUID, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// StringFromCLSID returns GUID formated string from GUID object.
|
||||
func StringFromCLSID(clsid *GUID) (string, error) {
|
||||
return "", NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// IIDFromString returns GUID from program ID.
|
||||
func IIDFromString(progId string) (*GUID, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// StringFromIID returns GUID formatted string from GUID object.
|
||||
func StringFromIID(iid *GUID) (string, error) {
|
||||
return "", NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CreateInstance of single uninitialized object with GUID.
|
||||
func CreateInstance(clsid *GUID, iid *GUID) (*IUnknown, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// GetActiveObject retrieves pointer to active object.
|
||||
func GetActiveObject(clsid *GUID, iid *GUID) (*IUnknown, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// VariantInit initializes variant.
|
||||
func VariantInit(v *VARIANT) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// VariantClear clears value in Variant settings to VT_EMPTY.
|
||||
func VariantClear(v *VARIANT) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// SysAllocString allocates memory for string and copies string into memory.
|
||||
func SysAllocString(v string) *int16 {
|
||||
u := int16(0)
|
||||
return &u
|
||||
}
|
||||
|
||||
// SysAllocStringLen copies up to length of given string returning pointer.
|
||||
func SysAllocStringLen(v string) *int16 {
|
||||
u := int16(0)
|
||||
return &u
|
||||
}
|
||||
|
||||
// SysFreeString frees string system memory. This must be called with SysAllocString.
|
||||
func SysFreeString(v *int16) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// SysStringLen is the length of the system allocated string.
|
||||
func SysStringLen(v *int16) uint32 {
|
||||
return uint32(0)
|
||||
}
|
||||
|
||||
// CreateStdDispatch provides default IDispatch implementation for IUnknown.
|
||||
//
|
||||
// This handles default IDispatch implementation for objects. It haves a few
|
||||
// limitations with only supporting one language. It will also only return
|
||||
// default exception codes.
|
||||
func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (*IDispatch, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
|
||||
//
|
||||
// This will not handle the full implementation of the interface.
|
||||
func CreateDispTypeInfo(idata *INTERFACEDATA) (*IUnknown, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// copyMemory moves location of a block of memory.
|
||||
func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {}
|
||||
|
||||
// GetUserDefaultLCID retrieves current user default locale.
|
||||
func GetUserDefaultLCID() uint32 {
|
||||
return uint32(0)
|
||||
}
|
||||
|
||||
// GetMessage in message queue from runtime.
|
||||
//
|
||||
// This function appears to block. PeekMessage does not block.
|
||||
func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (int32, error) {
|
||||
return int32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// DispatchMessage to window procedure.
|
||||
func DispatchMessage(msg *Msg) int32 {
|
||||
return int32(0)
|
||||
}
|
||||
|
||||
func GetVariantDate(value uint64) (time.Time, error) {
|
||||
return time.Now(), NewError(E_NOTIMPL)
|
||||
}
|
|
@ -1,192 +0,0 @@
|
|||
package ole
|
||||
|
||||
// Connection contains IUnknown for fluent interface interaction.
|
||||
//
|
||||
// Deprecated. Use oleutil package instead.
|
||||
type Connection struct {
|
||||
Object *IUnknown // Access COM
|
||||
}
|
||||
|
||||
// Initialize COM.
|
||||
func (*Connection) Initialize() (err error) {
|
||||
return coInitialize()
|
||||
}
|
||||
|
||||
// Uninitialize COM.
|
||||
func (*Connection) Uninitialize() {
|
||||
CoUninitialize()
|
||||
}
|
||||
|
||||
// Create IUnknown object based first on ProgId and then from String.
|
||||
func (c *Connection) Create(progId string) (err error) {
|
||||
var clsid *GUID
|
||||
clsid, err = CLSIDFromProgID(progId)
|
||||
if err != nil {
|
||||
clsid, err = CLSIDFromString(progId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
unknown, err := CreateInstance(clsid, IID_IUnknown)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.Object = unknown
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Release IUnknown object.
|
||||
func (c *Connection) Release() {
|
||||
c.Object.Release()
|
||||
}
|
||||
|
||||
// Load COM object from list of programIDs or strings.
|
||||
func (c *Connection) Load(names ...string) (errors []error) {
|
||||
var tempErrors []error = make([]error, len(names))
|
||||
var numErrors int = 0
|
||||
for _, name := range names {
|
||||
err := c.Create(name)
|
||||
if err != nil {
|
||||
tempErrors = append(tempErrors, err)
|
||||
numErrors += 1
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
copy(errors, tempErrors[0:numErrors])
|
||||
return
|
||||
}
|
||||
|
||||
// Dispatch returns Dispatch object.
|
||||
func (c *Connection) Dispatch() (object *Dispatch, err error) {
|
||||
dispatch, err := c.Object.QueryInterface(IID_IDispatch)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
object = &Dispatch{dispatch}
|
||||
return
|
||||
}
|
||||
|
||||
// Dispatch stores IDispatch object.
|
||||
type Dispatch struct {
|
||||
Object *IDispatch // Dispatch object.
|
||||
}
|
||||
|
||||
// Call method on IDispatch with parameters.
|
||||
func (d *Dispatch) Call(method string, params ...interface{}) (result *VARIANT, err error) {
|
||||
id, err := d.GetId(method)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result, err = d.Invoke(id, DISPATCH_METHOD, params)
|
||||
return
|
||||
}
|
||||
|
||||
// MustCall method on IDispatch with parameters.
|
||||
func (d *Dispatch) MustCall(method string, params ...interface{}) (result *VARIANT) {
|
||||
id, err := d.GetId(method)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
result, err = d.Invoke(id, DISPATCH_METHOD, params)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Get property on IDispatch with parameters.
|
||||
func (d *Dispatch) Get(name string, params ...interface{}) (result *VARIANT, err error) {
|
||||
id, err := d.GetId(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params)
|
||||
return
|
||||
}
|
||||
|
||||
// MustGet property on IDispatch with parameters.
|
||||
func (d *Dispatch) MustGet(name string, params ...interface{}) (result *VARIANT) {
|
||||
id, err := d.GetId(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Set property on IDispatch with parameters.
|
||||
func (d *Dispatch) Set(name string, params ...interface{}) (result *VARIANT, err error) {
|
||||
id, err := d.GetId(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params)
|
||||
return
|
||||
}
|
||||
|
||||
// MustSet property on IDispatch with parameters.
|
||||
func (d *Dispatch) MustSet(name string, params ...interface{}) (result *VARIANT) {
|
||||
id, err := d.GetId(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetId retrieves ID of name on IDispatch.
|
||||
func (d *Dispatch) GetId(name string) (id int32, err error) {
|
||||
var dispid []int32
|
||||
dispid, err = d.Object.GetIDsOfName([]string{name})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
id = dispid[0]
|
||||
return
|
||||
}
|
||||
|
||||
// GetIds retrieves all IDs of names on IDispatch.
|
||||
func (d *Dispatch) GetIds(names ...string) (dispid []int32, err error) {
|
||||
dispid, err = d.Object.GetIDsOfName(names)
|
||||
return
|
||||
}
|
||||
|
||||
// Invoke IDispatch on DisplayID of dispatch type with parameters.
|
||||
//
|
||||
// There have been problems where if send cascading params..., it would error
|
||||
// out because the parameters would be empty.
|
||||
func (d *Dispatch) Invoke(id int32, dispatch int16, params []interface{}) (result *VARIANT, err error) {
|
||||
if len(params) < 1 {
|
||||
result, err = d.Object.Invoke(id, dispatch)
|
||||
} else {
|
||||
result, err = d.Object.Invoke(id, dispatch, params...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Release IDispatch object.
|
||||
func (d *Dispatch) Release() {
|
||||
d.Object.Release()
|
||||
}
|
||||
|
||||
// Connect initializes COM and attempts to load IUnknown based on given names.
|
||||
func Connect(names ...string) (connection *Connection) {
|
||||
connection.Initialize()
|
||||
connection.Load(names...)
|
||||
return
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
package ole
|
||||
|
||||
const (
|
||||
CLSCTX_INPROC_SERVER = 1
|
||||
CLSCTX_INPROC_HANDLER = 2
|
||||
CLSCTX_LOCAL_SERVER = 4
|
||||
CLSCTX_INPROC_SERVER16 = 8
|
||||
CLSCTX_REMOTE_SERVER = 16
|
||||
CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER
|
||||
CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER
|
||||
CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
|
||||
)
|
||||
|
||||
const (
|
||||
COINIT_APARTMENTTHREADED = 0x2
|
||||
COINIT_MULTITHREADED = 0x0
|
||||
COINIT_DISABLE_OLE1DDE = 0x4
|
||||
COINIT_SPEED_OVER_MEMORY = 0x8
|
||||
)
|
||||
|
||||
const (
|
||||
DISPATCH_METHOD = 1
|
||||
DISPATCH_PROPERTYGET = 2
|
||||
DISPATCH_PROPERTYPUT = 4
|
||||
DISPATCH_PROPERTYPUTREF = 8
|
||||
)
|
||||
|
||||
const (
|
||||
S_OK = 0x00000000
|
||||
E_UNEXPECTED = 0x8000FFFF
|
||||
E_NOTIMPL = 0x80004001
|
||||
E_OUTOFMEMORY = 0x8007000E
|
||||
E_INVALIDARG = 0x80070057
|
||||
E_NOINTERFACE = 0x80004002
|
||||
E_POINTER = 0x80004003
|
||||
E_HANDLE = 0x80070006
|
||||
E_ABORT = 0x80004004
|
||||
E_FAIL = 0x80004005
|
||||
E_ACCESSDENIED = 0x80070005
|
||||
E_PENDING = 0x8000000A
|
||||
|
||||
CO_E_CLASSSTRING = 0x800401F3
|
||||
)
|
||||
|
||||
const (
|
||||
CC_FASTCALL = iota
|
||||
CC_CDECL
|
||||
CC_MSCPASCAL
|
||||
CC_PASCAL = CC_MSCPASCAL
|
||||
CC_MACPASCAL
|
||||
CC_STDCALL
|
||||
CC_FPFASTCALL
|
||||
CC_SYSCALL
|
||||
CC_MPWCDECL
|
||||
CC_MPWPASCAL
|
||||
CC_MAX = CC_MPWPASCAL
|
||||
)
|
||||
|
||||
type VT uint16
|
||||
|
||||
const (
|
||||
VT_EMPTY VT = 0x0
|
||||
VT_NULL VT = 0x1
|
||||
VT_I2 VT = 0x2
|
||||
VT_I4 VT = 0x3
|
||||
VT_R4 VT = 0x4
|
||||
VT_R8 VT = 0x5
|
||||
VT_CY VT = 0x6
|
||||
VT_DATE VT = 0x7
|
||||
VT_BSTR VT = 0x8
|
||||
VT_DISPATCH VT = 0x9
|
||||
VT_ERROR VT = 0xa
|
||||
VT_BOOL VT = 0xb
|
||||
VT_VARIANT VT = 0xc
|
||||
VT_UNKNOWN VT = 0xd
|
||||
VT_DECIMAL VT = 0xe
|
||||
VT_I1 VT = 0x10
|
||||
VT_UI1 VT = 0x11
|
||||
VT_UI2 VT = 0x12
|
||||
VT_UI4 VT = 0x13
|
||||
VT_I8 VT = 0x14
|
||||
VT_UI8 VT = 0x15
|
||||
VT_INT VT = 0x16
|
||||
VT_UINT VT = 0x17
|
||||
VT_VOID VT = 0x18
|
||||
VT_HRESULT VT = 0x19
|
||||
VT_PTR VT = 0x1a
|
||||
VT_SAFEARRAY VT = 0x1b
|
||||
VT_CARRAY VT = 0x1c
|
||||
VT_USERDEFINED VT = 0x1d
|
||||
VT_LPSTR VT = 0x1e
|
||||
VT_LPWSTR VT = 0x1f
|
||||
VT_RECORD VT = 0x24
|
||||
VT_INT_PTR VT = 0x25
|
||||
VT_UINT_PTR VT = 0x26
|
||||
VT_FILETIME VT = 0x40
|
||||
VT_BLOB VT = 0x41
|
||||
VT_STREAM VT = 0x42
|
||||
VT_STORAGE VT = 0x43
|
||||
VT_STREAMED_OBJECT VT = 0x44
|
||||
VT_STORED_OBJECT VT = 0x45
|
||||
VT_BLOB_OBJECT VT = 0x46
|
||||
VT_CF VT = 0x47
|
||||
VT_CLSID VT = 0x48
|
||||
VT_BSTR_BLOB VT = 0xfff
|
||||
VT_VECTOR VT = 0x1000
|
||||
VT_ARRAY VT = 0x2000
|
||||
VT_BYREF VT = 0x4000
|
||||
VT_RESERVED VT = 0x8000
|
||||
VT_ILLEGAL VT = 0xffff
|
||||
VT_ILLEGALMASKED VT = 0xfff
|
||||
VT_TYPEMASK VT = 0xfff
|
||||
)
|
||||
|
||||
const (
|
||||
DISPID_UNKNOWN = -1
|
||||
DISPID_VALUE = 0
|
||||
DISPID_PROPERTYPUT = -3
|
||||
DISPID_NEWENUM = -4
|
||||
DISPID_EVALUATE = -5
|
||||
DISPID_CONSTRUCTOR = -6
|
||||
DISPID_DESTRUCTOR = -7
|
||||
DISPID_COLLECT = -8
|
||||
)
|
||||
|
||||
const (
|
||||
TKIND_ENUM = 1
|
||||
TKIND_RECORD = 2
|
||||
TKIND_MODULE = 3
|
||||
TKIND_INTERFACE = 4
|
||||
TKIND_DISPATCH = 5
|
||||
TKIND_COCLASS = 6
|
||||
TKIND_ALIAS = 7
|
||||
TKIND_UNION = 8
|
||||
TKIND_MAX = 9
|
||||
)
|
||||
|
||||
// Safe Array Feature Flags
|
||||
|
||||
const (
|
||||
FADF_AUTO = 0x0001
|
||||
FADF_STATIC = 0x0002
|
||||
FADF_EMBEDDED = 0x0004
|
||||
FADF_FIXEDSIZE = 0x0010
|
||||
FADF_RECORD = 0x0020
|
||||
FADF_HAVEIID = 0x0040
|
||||
FADF_HAVEVARTYPE = 0x0080
|
||||
FADF_BSTR = 0x0100
|
||||
FADF_UNKNOWN = 0x0200
|
||||
FADF_DISPATCH = 0x0400
|
||||
FADF_VARIANT = 0x0800
|
||||
FADF_RESERVED = 0xF008
|
||||
)
|
|
@ -1,51 +0,0 @@
|
|||
package ole
|
||||
|
||||
// OleError stores COM errors.
|
||||
type OleError struct {
|
||||
hr uintptr
|
||||
description string
|
||||
subError error
|
||||
}
|
||||
|
||||
// NewError creates new error with HResult.
|
||||
func NewError(hr uintptr) *OleError {
|
||||
return &OleError{hr: hr}
|
||||
}
|
||||
|
||||
// NewErrorWithDescription creates new COM error with HResult and description.
|
||||
func NewErrorWithDescription(hr uintptr, description string) *OleError {
|
||||
return &OleError{hr: hr, description: description}
|
||||
}
|
||||
|
||||
// NewErrorWithSubError creates new COM error with parent error.
|
||||
func NewErrorWithSubError(hr uintptr, description string, err error) *OleError {
|
||||
return &OleError{hr: hr, description: description, subError: err}
|
||||
}
|
||||
|
||||
// Code is the HResult.
|
||||
func (v *OleError) Code() uintptr {
|
||||
return uintptr(v.hr)
|
||||
}
|
||||
|
||||
// String description, either manually set or format message with error code.
|
||||
func (v *OleError) String() string {
|
||||
if v.description != "" {
|
||||
return errstr(int(v.hr)) + " (" + v.description + ")"
|
||||
}
|
||||
return errstr(int(v.hr))
|
||||
}
|
||||
|
||||
// Error implements error interface.
|
||||
func (v *OleError) Error() string {
|
||||
return v.String()
|
||||
}
|
||||
|
||||
// Description retrieves error summary, if there is one.
|
||||
func (v *OleError) Description() string {
|
||||
return v.description
|
||||
}
|
||||
|
||||
// SubError returns parent error, if there is one.
|
||||
func (v *OleError) SubError() error {
|
||||
return v.subError
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
// errstr converts error code to string.
|
||||
func errstr(errno int) string {
|
||||
return ""
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
// errstr converts error code to string.
|
||||
func errstr(errno int) string {
|
||||
// ask windows for the remaining errors
|
||||
var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS
|
||||
b := make([]uint16, 300)
|
||||
n, err := syscall.FormatMessage(flags, 0, uint32(errno), 0, b, nil)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("error %d (FormatMessage failed with: %v)", errno, err)
|
||||
}
|
||||
// trim terminating \r and \n
|
||||
for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- {
|
||||
}
|
||||
return string(utf16.Decode(b[:n]))
|
||||
}
|
|
@ -1,284 +0,0 @@
|
|||
package ole
|
||||
|
||||
var (
|
||||
// IID_NULL is null Interface ID, used when no other Interface ID is known.
|
||||
IID_NULL = NewGUID("{00000000-0000-0000-0000-000000000000}")
|
||||
|
||||
// IID_IUnknown is for IUnknown interfaces.
|
||||
IID_IUnknown = NewGUID("{00000000-0000-0000-C000-000000000046}")
|
||||
|
||||
// IID_IDispatch is for IDispatch interfaces.
|
||||
IID_IDispatch = NewGUID("{00020400-0000-0000-C000-000000000046}")
|
||||
|
||||
// IID_IEnumVariant is for IEnumVariant interfaces
|
||||
IID_IEnumVariant = NewGUID("{00020404-0000-0000-C000-000000000046}")
|
||||
|
||||
// IID_IConnectionPointContainer is for IConnectionPointContainer interfaces.
|
||||
IID_IConnectionPointContainer = NewGUID("{B196B284-BAB4-101A-B69C-00AA00341D07}")
|
||||
|
||||
// IID_IConnectionPoint is for IConnectionPoint interfaces.
|
||||
IID_IConnectionPoint = NewGUID("{B196B286-BAB4-101A-B69C-00AA00341D07}")
|
||||
|
||||
// IID_IInspectable is for IInspectable interfaces.
|
||||
IID_IInspectable = NewGUID("{AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}")
|
||||
|
||||
// IID_IProvideClassInfo is for IProvideClassInfo interfaces.
|
||||
IID_IProvideClassInfo = NewGUID("{B196B283-BAB4-101A-B69C-00AA00341D07}")
|
||||
)
|
||||
|
||||
// These are for testing and not part of any library.
|
||||
var (
|
||||
// IID_ICOMTestString is for ICOMTestString interfaces.
|
||||
//
|
||||
// {E0133EB4-C36F-469A-9D3D-C66B84BE19ED}
|
||||
IID_ICOMTestString = NewGUID("{E0133EB4-C36F-469A-9D3D-C66B84BE19ED}")
|
||||
|
||||
// IID_ICOMTestInt8 is for ICOMTestInt8 interfaces.
|
||||
//
|
||||
// {BEB06610-EB84-4155-AF58-E2BFF53680B4}
|
||||
IID_ICOMTestInt8 = NewGUID("{BEB06610-EB84-4155-AF58-E2BFF53680B4}")
|
||||
|
||||
// IID_ICOMTestInt16 is for ICOMTestInt16 interfaces.
|
||||
//
|
||||
// {DAA3F9FA-761E-4976-A860-8364CE55F6FC}
|
||||
IID_ICOMTestInt16 = NewGUID("{DAA3F9FA-761E-4976-A860-8364CE55F6FC}")
|
||||
|
||||
// IID_ICOMTestInt32 is for ICOMTestInt32 interfaces.
|
||||
//
|
||||
// {E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}
|
||||
IID_ICOMTestInt32 = NewGUID("{E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}")
|
||||
|
||||
// IID_ICOMTestInt64 is for ICOMTestInt64 interfaces.
|
||||
//
|
||||
// {8D437CBC-B3ED-485C-BC32-C336432A1623}
|
||||
IID_ICOMTestInt64 = NewGUID("{8D437CBC-B3ED-485C-BC32-C336432A1623}")
|
||||
|
||||
// IID_ICOMTestFloat is for ICOMTestFloat interfaces.
|
||||
//
|
||||
// {BF1ED004-EA02-456A-AA55-2AC8AC6B054C}
|
||||
IID_ICOMTestFloat = NewGUID("{BF1ED004-EA02-456A-AA55-2AC8AC6B054C}")
|
||||
|
||||
// IID_ICOMTestDouble is for ICOMTestDouble interfaces.
|
||||
//
|
||||
// {BF908A81-8687-4E93-999F-D86FAB284BA0}
|
||||
IID_ICOMTestDouble = NewGUID("{BF908A81-8687-4E93-999F-D86FAB284BA0}")
|
||||
|
||||
// IID_ICOMTestBoolean is for ICOMTestBoolean interfaces.
|
||||
//
|
||||
// {D530E7A6-4EE8-40D1-8931-3D63B8605010}
|
||||
IID_ICOMTestBoolean = NewGUID("{D530E7A6-4EE8-40D1-8931-3D63B8605010}")
|
||||
|
||||
// IID_ICOMEchoTestObject is for ICOMEchoTestObject interfaces.
|
||||
//
|
||||
// {6485B1EF-D780-4834-A4FE-1EBB51746CA3}
|
||||
IID_ICOMEchoTestObject = NewGUID("{6485B1EF-D780-4834-A4FE-1EBB51746CA3}")
|
||||
|
||||
// IID_ICOMTestTypes is for ICOMTestTypes interfaces.
|
||||
//
|
||||
// {CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}
|
||||
IID_ICOMTestTypes = NewGUID("{CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}")
|
||||
|
||||
// CLSID_COMEchoTestObject is for COMEchoTestObject class.
|
||||
//
|
||||
// {3C24506A-AE9E-4D50-9157-EF317281F1B0}
|
||||
CLSID_COMEchoTestObject = NewGUID("{3C24506A-AE9E-4D50-9157-EF317281F1B0}")
|
||||
|
||||
// CLSID_COMTestScalarClass is for COMTestScalarClass class.
|
||||
//
|
||||
// {865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}
|
||||
CLSID_COMTestScalarClass = NewGUID("{865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}")
|
||||
)
|
||||
|
||||
const hextable = "0123456789ABCDEF"
|
||||
const emptyGUID = "{00000000-0000-0000-0000-000000000000}"
|
||||
|
||||
// GUID is Windows API specific GUID type.
|
||||
//
|
||||
// This exists to match Windows GUID type for direct passing for COM.
|
||||
// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx.
|
||||
type GUID struct {
|
||||
Data1 uint32
|
||||
Data2 uint16
|
||||
Data3 uint16
|
||||
Data4 [8]byte
|
||||
}
|
||||
|
||||
// NewGUID converts the given string into a globally unique identifier that is
|
||||
// compliant with the Windows API.
|
||||
//
|
||||
// The supplied string may be in any of these formats:
|
||||
//
|
||||
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
|
||||
//
|
||||
// The conversion of the supplied string is not case-sensitive.
|
||||
func NewGUID(guid string) *GUID {
|
||||
d := []byte(guid)
|
||||
var d1, d2, d3, d4a, d4b []byte
|
||||
|
||||
switch len(d) {
|
||||
case 38:
|
||||
if d[0] != '{' || d[37] != '}' {
|
||||
return nil
|
||||
}
|
||||
d = d[1:37]
|
||||
fallthrough
|
||||
case 36:
|
||||
if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' {
|
||||
return nil
|
||||
}
|
||||
d1 = d[0:8]
|
||||
d2 = d[9:13]
|
||||
d3 = d[14:18]
|
||||
d4a = d[19:23]
|
||||
d4b = d[24:36]
|
||||
case 32:
|
||||
d1 = d[0:8]
|
||||
d2 = d[8:12]
|
||||
d3 = d[12:16]
|
||||
d4a = d[16:20]
|
||||
d4b = d[20:32]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
var g GUID
|
||||
var ok1, ok2, ok3, ok4 bool
|
||||
g.Data1, ok1 = decodeHexUint32(d1)
|
||||
g.Data2, ok2 = decodeHexUint16(d2)
|
||||
g.Data3, ok3 = decodeHexUint16(d3)
|
||||
g.Data4, ok4 = decodeHexByte64(d4a, d4b)
|
||||
if ok1 && ok2 && ok3 && ok4 {
|
||||
return &g
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeHexUint32(src []byte) (value uint32, ok bool) {
|
||||
var b1, b2, b3, b4 byte
|
||||
var ok1, ok2, ok3, ok4 bool
|
||||
b1, ok1 = decodeHexByte(src[0], src[1])
|
||||
b2, ok2 = decodeHexByte(src[2], src[3])
|
||||
b3, ok3 = decodeHexByte(src[4], src[5])
|
||||
b4, ok4 = decodeHexByte(src[6], src[7])
|
||||
value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4)
|
||||
ok = ok1 && ok2 && ok3 && ok4
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexUint16(src []byte) (value uint16, ok bool) {
|
||||
var b1, b2 byte
|
||||
var ok1, ok2 bool
|
||||
b1, ok1 = decodeHexByte(src[0], src[1])
|
||||
b2, ok2 = decodeHexByte(src[2], src[3])
|
||||
value = (uint16(b1) << 8) | uint16(b2)
|
||||
ok = ok1 && ok2
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) {
|
||||
var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool
|
||||
value[0], ok1 = decodeHexByte(s1[0], s1[1])
|
||||
value[1], ok2 = decodeHexByte(s1[2], s1[3])
|
||||
value[2], ok3 = decodeHexByte(s2[0], s2[1])
|
||||
value[3], ok4 = decodeHexByte(s2[2], s2[3])
|
||||
value[4], ok5 = decodeHexByte(s2[4], s2[5])
|
||||
value[5], ok6 = decodeHexByte(s2[6], s2[7])
|
||||
value[6], ok7 = decodeHexByte(s2[8], s2[9])
|
||||
value[7], ok8 = decodeHexByte(s2[10], s2[11])
|
||||
ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexByte(c1, c2 byte) (value byte, ok bool) {
|
||||
var n1, n2 byte
|
||||
var ok1, ok2 bool
|
||||
n1, ok1 = decodeHexChar(c1)
|
||||
n2, ok2 = decodeHexChar(c2)
|
||||
value = (n1 << 4) | n2
|
||||
ok = ok1 && ok2
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexChar(c byte) (byte, bool) {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return c - '0', true
|
||||
case 'a' <= c && c <= 'f':
|
||||
return c - 'a' + 10, true
|
||||
case 'A' <= c && c <= 'F':
|
||||
return c - 'A' + 10, true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// String converts the GUID to string form. It will adhere to this pattern:
|
||||
//
|
||||
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
|
||||
//
|
||||
// If the GUID is nil, the string representation of an empty GUID is returned:
|
||||
//
|
||||
// {00000000-0000-0000-0000-000000000000}
|
||||
func (guid *GUID) String() string {
|
||||
if guid == nil {
|
||||
return emptyGUID
|
||||
}
|
||||
|
||||
var c [38]byte
|
||||
c[0] = '{'
|
||||
putUint32Hex(c[1:9], guid.Data1)
|
||||
c[9] = '-'
|
||||
putUint16Hex(c[10:14], guid.Data2)
|
||||
c[14] = '-'
|
||||
putUint16Hex(c[15:19], guid.Data3)
|
||||
c[19] = '-'
|
||||
putByteHex(c[20:24], guid.Data4[0:2])
|
||||
c[24] = '-'
|
||||
putByteHex(c[25:37], guid.Data4[2:8])
|
||||
c[37] = '}'
|
||||
return string(c[:])
|
||||
}
|
||||
|
||||
func putUint32Hex(b []byte, v uint32) {
|
||||
b[0] = hextable[byte(v>>24)>>4]
|
||||
b[1] = hextable[byte(v>>24)&0x0f]
|
||||
b[2] = hextable[byte(v>>16)>>4]
|
||||
b[3] = hextable[byte(v>>16)&0x0f]
|
||||
b[4] = hextable[byte(v>>8)>>4]
|
||||
b[5] = hextable[byte(v>>8)&0x0f]
|
||||
b[6] = hextable[byte(v)>>4]
|
||||
b[7] = hextable[byte(v)&0x0f]
|
||||
}
|
||||
|
||||
func putUint16Hex(b []byte, v uint16) {
|
||||
b[0] = hextable[byte(v>>8)>>4]
|
||||
b[1] = hextable[byte(v>>8)&0x0f]
|
||||
b[2] = hextable[byte(v)>>4]
|
||||
b[3] = hextable[byte(v)&0x0f]
|
||||
}
|
||||
|
||||
func putByteHex(dst, src []byte) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i*2] = hextable[src[i]>>4]
|
||||
dst[i*2+1] = hextable[src[i]&0x0f]
|
||||
}
|
||||
}
|
||||
|
||||
// IsEqualGUID compares two GUID.
|
||||
//
|
||||
// Not constant time comparison.
|
||||
func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool {
|
||||
return guid1.Data1 == guid2.Data1 &&
|
||||
guid1.Data2 == guid2.Data2 &&
|
||||
guid1.Data3 == guid2.Data3 &&
|
||||
guid1.Data4[0] == guid2.Data4[0] &&
|
||||
guid1.Data4[1] == guid2.Data4[1] &&
|
||||
guid1.Data4[2] == guid2.Data4[2] &&
|
||||
guid1.Data4[3] == guid2.Data4[3] &&
|
||||
guid1.Data4[4] == guid2.Data4[4] &&
|
||||
guid1.Data4[5] == guid2.Data4[5] &&
|
||||
guid1.Data4[6] == guid2.Data4[6] &&
|
||||
guid1.Data4[7] == guid2.Data4[7]
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IConnectionPoint struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IConnectionPointVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetConnectionInterface uintptr
|
||||
GetConnectionPointContainer uintptr
|
||||
Advise uintptr
|
||||
Unadvise uintptr
|
||||
EnumConnections uintptr
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) VTable() *IConnectionPointVtbl {
|
||||
return (*IConnectionPointVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 {
|
||||
return int32(0)
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) Advise(unknown *IUnknown) (uint32, error) {
|
||||
return uint32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) Unadvise(cookie uint32) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) (err error) {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 {
|
||||
// XXX: This doesn't look like it does what it's supposed to
|
||||
return release((*IUnknown)(unsafe.Pointer(v)))
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) Advise(unknown *IUnknown) (cookie uint32, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().Advise,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(unknown)),
|
||||
uintptr(unsafe.Pointer(&cookie)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) Unadvise(cookie uint32) (err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().Unadvise,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(cookie),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IConnectionPointContainer struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IConnectionPointContainerVtbl struct {
|
||||
IUnknownVtbl
|
||||
EnumConnectionPoints uintptr
|
||||
FindConnectionPoint uintptr
|
||||
}
|
||||
|
||||
func (v *IConnectionPointContainer) VTable() *IConnectionPointContainerVtbl {
|
||||
return (*IConnectionPointContainerVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) (err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().FindConnectionPoint,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(point)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IDispatch struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IDispatchVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetTypeInfoCount uintptr
|
||||
GetTypeInfo uintptr
|
||||
GetIDsOfNames uintptr
|
||||
Invoke uintptr
|
||||
}
|
||||
|
||||
func (v *IDispatch) VTable() *IDispatchVtbl {
|
||||
return (*IDispatchVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
|
||||
func (v *IDispatch) GetIDsOfName(names []string) (dispid []int32, err error) {
|
||||
dispid, err = getIDsOfName(v, names)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IDispatch) Invoke(dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) {
|
||||
result, err = invoke(v, dispid, dispatch, params...)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IDispatch) GetTypeInfoCount() (c uint32, err error) {
|
||||
c, err = getTypeInfoCount(v)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IDispatch) GetTypeInfo() (tinfo *ITypeInfo, err error) {
|
||||
tinfo, err = getTypeInfo(v)
|
||||
return
|
||||
}
|
||||
|
||||
// GetSingleIDOfName is a helper that returns single display ID for IDispatch name.
|
||||
//
|
||||
// This replaces the common pattern of attempting to get a single name from the list of available
|
||||
// IDs. It gives the first ID, if it is available.
|
||||
func (v *IDispatch) GetSingleIDOfName(name string) (displayID int32, err error) {
|
||||
var displayIDs []int32
|
||||
displayIDs, err = v.GetIDsOfName([]string{name})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
displayID = displayIDs[0]
|
||||
return
|
||||
}
|
||||
|
||||
// InvokeWithOptionalArgs accepts arguments as an array, works like Invoke.
|
||||
//
|
||||
// Accepts name and will attempt to retrieve Display ID to pass to Invoke.
|
||||
//
|
||||
// Passing params as an array is a workaround that could be fixed in later versions of Go that
|
||||
// prevent passing empty params. During testing it was discovered that this is an acceptable way of
|
||||
// getting around not being able to pass params normally.
|
||||
func (v *IDispatch) InvokeWithOptionalArgs(name string, dispatch int16, params []interface{}) (result *VARIANT, err error) {
|
||||
displayID, err := v.GetSingleIDOfName(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(params) < 1 {
|
||||
result, err = v.Invoke(displayID, dispatch)
|
||||
} else {
|
||||
result, err = v.Invoke(displayID, dispatch, params...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CallMethod invokes named function with arguments on object.
|
||||
func (v *IDispatch) CallMethod(name string, params ...interface{}) (*VARIANT, error) {
|
||||
return v.InvokeWithOptionalArgs(name, DISPATCH_METHOD, params)
|
||||
}
|
||||
|
||||
// GetProperty retrieves the property with the name with the ability to pass arguments.
|
||||
//
|
||||
// Most of the time you will not need to pass arguments as most objects do not allow for this
|
||||
// feature. Or at least, should not allow for this feature. Some servers don't follow best practices
|
||||
// and this is provided for those edge cases.
|
||||
func (v *IDispatch) GetProperty(name string, params ...interface{}) (*VARIANT, error) {
|
||||
return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYGET, params)
|
||||
}
|
||||
|
||||
// PutProperty attempts to mutate a property in the object.
|
||||
func (v *IDispatch) PutProperty(name string, params ...interface{}) (*VARIANT, error) {
|
||||
return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYPUT, params)
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func getIDsOfName(disp *IDispatch, names []string) ([]int32, error) {
|
||||
return []int32{}, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func getTypeInfoCount(disp *IDispatch) (uint32, error) {
|
||||
return uint32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func getTypeInfo(disp *IDispatch) (*ITypeInfo, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (*VARIANT, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getIDsOfName(disp *IDispatch, names []string) (dispid []int32, err error) {
|
||||
wnames := make([]*uint16, len(names))
|
||||
for i := 0; i < len(names); i++ {
|
||||
wnames[i] = syscall.StringToUTF16Ptr(names[i])
|
||||
}
|
||||
dispid = make([]int32, len(names))
|
||||
namelen := uint32(len(names))
|
||||
hr, _, _ := syscall.Syscall6(
|
||||
disp.VTable().GetIDsOfNames,
|
||||
6,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(unsafe.Pointer(IID_NULL)),
|
||||
uintptr(unsafe.Pointer(&wnames[0])),
|
||||
uintptr(namelen),
|
||||
uintptr(GetUserDefaultLCID()),
|
||||
uintptr(unsafe.Pointer(&dispid[0])))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getTypeInfoCount(disp *IDispatch) (c uint32, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
disp.VTable().GetTypeInfoCount,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(unsafe.Pointer(&c)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getTypeInfo(disp *IDispatch) (tinfo *ITypeInfo, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
disp.VTable().GetTypeInfo,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(GetUserDefaultLCID()),
|
||||
uintptr(unsafe.Pointer(&tinfo)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) {
|
||||
var dispparams DISPPARAMS
|
||||
|
||||
if dispatch&DISPATCH_PROPERTYPUT != 0 {
|
||||
dispnames := [1]int32{DISPID_PROPERTYPUT}
|
||||
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
|
||||
dispparams.cNamedArgs = 1
|
||||
} else if dispatch&DISPATCH_PROPERTYPUTREF != 0 {
|
||||
dispnames := [1]int32{DISPID_PROPERTYPUT}
|
||||
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
|
||||
dispparams.cNamedArgs = 1
|
||||
}
|
||||
var vargs []VARIANT
|
||||
if len(params) > 0 {
|
||||
vargs = make([]VARIANT, len(params))
|
||||
for i, v := range params {
|
||||
//n := len(params)-i-1
|
||||
n := len(params) - i - 1
|
||||
VariantInit(&vargs[n])
|
||||
switch vv := v.(type) {
|
||||
case bool:
|
||||
if vv {
|
||||
vargs[n] = NewVariant(VT_BOOL, 0xffff)
|
||||
} else {
|
||||
vargs[n] = NewVariant(VT_BOOL, 0)
|
||||
}
|
||||
case *bool:
|
||||
vargs[n] = NewVariant(VT_BOOL|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*bool)))))
|
||||
case uint8:
|
||||
vargs[n] = NewVariant(VT_I1, int64(v.(uint8)))
|
||||
case *uint8:
|
||||
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8)))))
|
||||
case int8:
|
||||
vargs[n] = NewVariant(VT_I1, int64(v.(int8)))
|
||||
case *int8:
|
||||
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8)))))
|
||||
case int16:
|
||||
vargs[n] = NewVariant(VT_I2, int64(v.(int16)))
|
||||
case *int16:
|
||||
vargs[n] = NewVariant(VT_I2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int16)))))
|
||||
case uint16:
|
||||
vargs[n] = NewVariant(VT_UI2, int64(v.(uint16)))
|
||||
case *uint16:
|
||||
vargs[n] = NewVariant(VT_UI2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint16)))))
|
||||
case int32:
|
||||
vargs[n] = NewVariant(VT_I4, int64(v.(int32)))
|
||||
case *int32:
|
||||
vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int32)))))
|
||||
case uint32:
|
||||
vargs[n] = NewVariant(VT_UI4, int64(v.(uint32)))
|
||||
case *uint32:
|
||||
vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint32)))))
|
||||
case int64:
|
||||
vargs[n] = NewVariant(VT_I8, int64(v.(int64)))
|
||||
case *int64:
|
||||
vargs[n] = NewVariant(VT_I8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int64)))))
|
||||
case uint64:
|
||||
vargs[n] = NewVariant(VT_UI8, int64(uintptr(v.(uint64))))
|
||||
case *uint64:
|
||||
vargs[n] = NewVariant(VT_UI8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint64)))))
|
||||
case int:
|
||||
vargs[n] = NewVariant(VT_I4, int64(v.(int)))
|
||||
case *int:
|
||||
vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int)))))
|
||||
case uint:
|
||||
vargs[n] = NewVariant(VT_UI4, int64(v.(uint)))
|
||||
case *uint:
|
||||
vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint)))))
|
||||
case float32:
|
||||
vargs[n] = NewVariant(VT_R4, *(*int64)(unsafe.Pointer(&vv)))
|
||||
case *float32:
|
||||
vargs[n] = NewVariant(VT_R4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float32)))))
|
||||
case float64:
|
||||
vargs[n] = NewVariant(VT_R8, *(*int64)(unsafe.Pointer(&vv)))
|
||||
case *float64:
|
||||
vargs[n] = NewVariant(VT_R8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float64)))))
|
||||
case *big.Int:
|
||||
vargs[n] = NewVariant(VT_DECIMAL, v.(*big.Int).Int64())
|
||||
case string:
|
||||
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(v.(string))))))
|
||||
case *string:
|
||||
vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*string)))))
|
||||
case time.Time:
|
||||
s := vv.Format("2006-01-02 15:04:05")
|
||||
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(s)))))
|
||||
case *time.Time:
|
||||
s := vv.Format("2006-01-02 15:04:05")
|
||||
vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(&s))))
|
||||
case *IDispatch:
|
||||
vargs[n] = NewVariant(VT_DISPATCH, int64(uintptr(unsafe.Pointer(v.(*IDispatch)))))
|
||||
case **IDispatch:
|
||||
vargs[n] = NewVariant(VT_DISPATCH|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(**IDispatch)))))
|
||||
case nil:
|
||||
vargs[n] = NewVariant(VT_NULL, 0)
|
||||
case *VARIANT:
|
||||
vargs[n] = NewVariant(VT_VARIANT|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*VARIANT)))))
|
||||
case []byte:
|
||||
safeByteArray := safeArrayFromByteSlice(v.([]byte))
|
||||
vargs[n] = NewVariant(VT_ARRAY|VT_UI1, int64(uintptr(unsafe.Pointer(safeByteArray))))
|
||||
defer VariantClear(&vargs[n])
|
||||
case []string:
|
||||
safeByteArray := safeArrayFromStringSlice(v.([]string))
|
||||
vargs[n] = NewVariant(VT_ARRAY|VT_BSTR, int64(uintptr(unsafe.Pointer(safeByteArray))))
|
||||
defer VariantClear(&vargs[n])
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
}
|
||||
dispparams.rgvarg = uintptr(unsafe.Pointer(&vargs[0]))
|
||||
dispparams.cArgs = uint32(len(params))
|
||||
}
|
||||
|
||||
result = new(VARIANT)
|
||||
var excepInfo EXCEPINFO
|
||||
VariantInit(result)
|
||||
hr, _, _ := syscall.Syscall9(
|
||||
disp.VTable().Invoke,
|
||||
9,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(dispid),
|
||||
uintptr(unsafe.Pointer(IID_NULL)),
|
||||
uintptr(GetUserDefaultLCID()),
|
||||
uintptr(dispatch),
|
||||
uintptr(unsafe.Pointer(&dispparams)),
|
||||
uintptr(unsafe.Pointer(result)),
|
||||
uintptr(unsafe.Pointer(&excepInfo)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewErrorWithSubError(hr, BstrToString(excepInfo.bstrDescription), excepInfo)
|
||||
}
|
||||
for i, varg := range vargs {
|
||||
n := len(params) - i - 1
|
||||
if varg.VT == VT_BSTR && varg.Val != 0 {
|
||||
SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val)))))
|
||||
}
|
||||
if varg.VT == (VT_BSTR|VT_BYREF) && varg.Val != 0 {
|
||||
*(params[n].(*string)) = LpOleStrToString(*(**uint16)(unsafe.Pointer(uintptr(varg.Val))))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IEnumVARIANT struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IEnumVARIANTVtbl struct {
|
||||
IUnknownVtbl
|
||||
Next uintptr
|
||||
Skip uintptr
|
||||
Reset uintptr
|
||||
Clone uintptr
|
||||
}
|
||||
|
||||
func (v *IEnumVARIANT) VTable() *IEnumVARIANTVtbl {
|
||||
return (*IEnumVARIANTVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func (enum *IEnumVARIANT) Clone() (*IEnumVARIANT, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Reset() error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Skip(celt uint) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Next(celt uint) (VARIANT, uint, error) {
|
||||
return NewVariant(VT_NULL, int64(0)), 0, NewError(E_NOTIMPL)
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (enum *IEnumVARIANT) Clone() (cloned *IEnumVARIANT, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
enum.VTable().Clone,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(enum)),
|
||||
uintptr(unsafe.Pointer(&cloned)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Reset() (err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
enum.VTable().Reset,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(enum)),
|
||||
0,
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Skip(celt uint) (err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
enum.VTable().Skip,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(enum)),
|
||||
uintptr(celt),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Next(celt uint) (array VARIANT, length uint, err error) {
|
||||
hr, _, _ := syscall.Syscall6(
|
||||
enum.VTable().Next,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(enum)),
|
||||
uintptr(celt),
|
||||
uintptr(unsafe.Pointer(&array)),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IInspectable struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IInspectableVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetIIds uintptr
|
||||
GetRuntimeClassName uintptr
|
||||
GetTrustLevel uintptr
|
||||
}
|
||||
|
||||
func (v *IInspectable) VTable() *IInspectableVtbl {
|
||||
return (*IInspectableVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func (v *IInspectable) GetIids() ([]*GUID, error) {
|
||||
return []*GUID{}, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IInspectable) GetRuntimeClassName() (string, error) {
|
||||
return "", NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IInspectable) GetTrustLevel() (uint32, error) {
|
||||
return uint32(0), NewError(E_NOTIMPL)
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (v *IInspectable) GetIids() (iids []*GUID, err error) {
|
||||
var count uint32
|
||||
var array uintptr
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().GetIIds,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&count)),
|
||||
uintptr(unsafe.Pointer(&array)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
return
|
||||
}
|
||||
defer CoTaskMemFree(array)
|
||||
|
||||
iids = make([]*GUID, count)
|
||||
byteCount := count * uint32(unsafe.Sizeof(GUID{}))
|
||||
slicehdr := reflect.SliceHeader{Data: array, Len: int(byteCount), Cap: int(byteCount)}
|
||||
byteSlice := *(*[]byte)(unsafe.Pointer(&slicehdr))
|
||||
reader := bytes.NewReader(byteSlice)
|
||||
for i := range iids {
|
||||
guid := GUID{}
|
||||
err = binary.Read(reader, binary.LittleEndian, &guid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
iids[i] = &guid
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IInspectable) GetRuntimeClassName() (s string, err error) {
|
||||
var hstring HString
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().GetRuntimeClassName,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&hstring)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
return
|
||||
}
|
||||
s = hstring.String()
|
||||
DeleteHString(hstring)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IInspectable) GetTrustLevel() (level uint32, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().GetTrustLevel,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&level)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IProvideClassInfo struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IProvideClassInfoVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetClassInfo uintptr
|
||||
}
|
||||
|
||||
func (v *IProvideClassInfo) VTable() *IProvideClassInfoVtbl {
|
||||
return (*IProvideClassInfoVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
|
||||
func (v *IProvideClassInfo) GetClassInfo() (cinfo *ITypeInfo, err error) {
|
||||
cinfo, err = getClassInfo(v)
|
||||
return
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
disp.VTable().GetClassInfo,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(unsafe.Pointer(&tinfo)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type ITypeInfo struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type ITypeInfoVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetTypeAttr uintptr
|
||||
GetTypeComp uintptr
|
||||
GetFuncDesc uintptr
|
||||
GetVarDesc uintptr
|
||||
GetNames uintptr
|
||||
GetRefTypeOfImplType uintptr
|
||||
GetImplTypeFlags uintptr
|
||||
GetIDsOfNames uintptr
|
||||
Invoke uintptr
|
||||
GetDocumentation uintptr
|
||||
GetDllEntry uintptr
|
||||
GetRefTypeInfo uintptr
|
||||
AddressOfMember uintptr
|
||||
CreateInstance uintptr
|
||||
GetMops uintptr
|
||||
GetContainingTypeLib uintptr
|
||||
ReleaseTypeAttr uintptr
|
||||
ReleaseFuncDesc uintptr
|
||||
ReleaseVarDesc uintptr
|
||||
}
|
||||
|
||||
func (v *ITypeInfo) VTable() *ITypeInfoVtbl {
|
||||
return (*ITypeInfoVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func (v *ITypeInfo) GetTypeAttr() (*TYPEATTR, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (v *ITypeInfo) GetTypeAttr() (tattr *TYPEATTR, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
uintptr(v.VTable().GetTypeAttr),
|
||||
2,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&tattr)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IUnknown struct {
|
||||
RawVTable *interface{}
|
||||
}
|
||||
|
||||
type IUnknownVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
}
|
||||
|
||||
type UnknownLike interface {
|
||||
QueryInterface(iid *GUID) (disp *IDispatch, err error)
|
||||
AddRef() int32
|
||||
Release() int32
|
||||
}
|
||||
|
||||
func (v *IUnknown) VTable() *IUnknownVtbl {
|
||||
return (*IUnknownVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
|
||||
func (v *IUnknown) PutQueryInterface(interfaceID *GUID, obj interface{}) error {
|
||||
return reflectQueryInterface(v, v.VTable().QueryInterface, interfaceID, obj)
|
||||
}
|
||||
|
||||
func (v *IUnknown) IDispatch(interfaceID *GUID) (dispatch *IDispatch, err error) {
|
||||
err = v.PutQueryInterface(interfaceID, &dispatch)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IUnknown) IEnumVARIANT(interfaceID *GUID) (enum *IEnumVARIANT, err error) {
|
||||
err = v.PutQueryInterface(interfaceID, &enum)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IUnknown) QueryInterface(iid *GUID) (*IDispatch, error) {
|
||||
return queryInterface(v, iid)
|
||||
}
|
||||
|
||||
func (v *IUnknown) MustQueryInterface(iid *GUID) (disp *IDispatch) {
|
||||
unk, err := queryInterface(v, iid)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return unk
|
||||
}
|
||||
|
||||
func (v *IUnknown) AddRef() int32 {
|
||||
return addRef(v)
|
||||
}
|
||||
|
||||
func (v *IUnknown) Release() int32 {
|
||||
return release(v)
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func addRef(unk *IUnknown) int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func release(unk *IUnknown) int32 {
|
||||
return 0
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) {
|
||||
selfValue := reflect.ValueOf(self).Elem()
|
||||
objValue := reflect.ValueOf(obj).Elem()
|
||||
|
||||
hr, _, _ := syscall.Syscall(
|
||||
method,
|
||||
3,
|
||||
selfValue.UnsafeAddr(),
|
||||
uintptr(unsafe.Pointer(interfaceID)),
|
||||
objValue.Addr().Pointer())
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
unk.VTable().QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(unk)),
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(&disp)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func addRef(unk *IUnknown) int32 {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
unk.VTable().AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(unk)),
|
||||
0,
|
||||
0)
|
||||
return int32(ret)
|
||||
}
|
||||
|
||||
func release(unk *IUnknown) int32 {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
unk.VTable().Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(unk)),
|
||||
0,
|
||||
0)
|
||||
return int32(ret)
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
package ole
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DISPPARAMS are the arguments that passed to methods or property.
|
||||
type DISPPARAMS struct {
|
||||
rgvarg uintptr
|
||||
rgdispidNamedArgs uintptr
|
||||
cArgs uint32
|
||||
cNamedArgs uint32
|
||||
}
|
||||
|
||||
// EXCEPINFO defines exception info.
|
||||
type EXCEPINFO struct {
|
||||
wCode uint16
|
||||
wReserved uint16
|
||||
bstrSource *uint16
|
||||
bstrDescription *uint16
|
||||
bstrHelpFile *uint16
|
||||
dwHelpContext uint32
|
||||
pvReserved uintptr
|
||||
pfnDeferredFillIn uintptr
|
||||
scode uint32
|
||||
}
|
||||
|
||||
// WCode return wCode in EXCEPINFO.
|
||||
func (e EXCEPINFO) WCode() uint16 {
|
||||
return e.wCode
|
||||
}
|
||||
|
||||
// SCODE return scode in EXCEPINFO.
|
||||
func (e EXCEPINFO) SCODE() uint32 {
|
||||
return e.scode
|
||||
}
|
||||
|
||||
// String convert EXCEPINFO to string.
|
||||
func (e EXCEPINFO) String() string {
|
||||
var src, desc, hlp string
|
||||
if e.bstrSource == nil {
|
||||
src = "<nil>"
|
||||
} else {
|
||||
src = BstrToString(e.bstrSource)
|
||||
}
|
||||
|
||||
if e.bstrDescription == nil {
|
||||
desc = "<nil>"
|
||||
} else {
|
||||
desc = BstrToString(e.bstrDescription)
|
||||
}
|
||||
|
||||
if e.bstrHelpFile == nil {
|
||||
hlp = "<nil>"
|
||||
} else {
|
||||
hlp = BstrToString(e.bstrHelpFile)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"wCode: %#x, bstrSource: %v, bstrDescription: %v, bstrHelpFile: %v, dwHelpContext: %#x, scode: %#x",
|
||||
e.wCode, src, desc, hlp, e.dwHelpContext, e.scode,
|
||||
)
|
||||
}
|
||||
|
||||
// Error implements error interface and returns error string.
|
||||
func (e EXCEPINFO) Error() string {
|
||||
if e.bstrDescription != nil {
|
||||
return strings.TrimSpace(BstrToString(e.bstrDescription))
|
||||
}
|
||||
|
||||
src := "Unknown"
|
||||
if e.bstrSource != nil {
|
||||
src = BstrToString(e.bstrSource)
|
||||
}
|
||||
|
||||
code := e.scode
|
||||
if e.wCode != 0 {
|
||||
code = uint32(e.wCode)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v: %#x", src, code)
|
||||
}
|
||||
|
||||
// PARAMDATA defines parameter data type.
|
||||
type PARAMDATA struct {
|
||||
Name *int16
|
||||
Vt uint16
|
||||
}
|
||||
|
||||
// METHODDATA defines method info.
|
||||
type METHODDATA struct {
|
||||
Name *uint16
|
||||
Data *PARAMDATA
|
||||
Dispid int32
|
||||
Meth uint32
|
||||
CC int32
|
||||
CArgs uint32
|
||||
Flags uint16
|
||||
VtReturn uint32
|
||||
}
|
||||
|
||||
// INTERFACEDATA defines interface info.
|
||||
type INTERFACEDATA struct {
|
||||
MethodData *METHODDATA
|
||||
CMembers uint32
|
||||
}
|
||||
|
||||
// Point is 2D vector type.
|
||||
type Point struct {
|
||||
X int32
|
||||
Y int32
|
||||
}
|
||||
|
||||
// Msg is message between processes.
|
||||
type Msg struct {
|
||||
Hwnd uint32
|
||||
Message uint32
|
||||
Wparam int32
|
||||
Lparam int32
|
||||
Time uint32
|
||||
Pt Point
|
||||
}
|
||||
|
||||
// TYPEDESC defines data type.
|
||||
type TYPEDESC struct {
|
||||
Hreftype uint32
|
||||
VT uint16
|
||||
}
|
||||
|
||||
// IDLDESC defines IDL info.
|
||||
type IDLDESC struct {
|
||||
DwReserved uint32
|
||||
WIDLFlags uint16
|
||||
}
|
||||
|
||||
// TYPEATTR defines type info.
|
||||
type TYPEATTR struct {
|
||||
Guid GUID
|
||||
Lcid uint32
|
||||
dwReserved uint32
|
||||
MemidConstructor int32
|
||||
MemidDestructor int32
|
||||
LpstrSchema *uint16
|
||||
CbSizeInstance uint32
|
||||
Typekind int32
|
||||
CFuncs uint16
|
||||
CVars uint16
|
||||
CImplTypes uint16
|
||||
CbSizeVft uint16
|
||||
CbAlignment uint16
|
||||
WTypeFlags uint16
|
||||
WMajorVerNum uint16
|
||||
WMinorVerNum uint16
|
||||
TdescAlias TYPEDESC
|
||||
IdldescType IDLDESC
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package oleutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
ole "github.com/gofiber/fiber/v3/internal/go-ole"
|
||||
)
|
||||
|
||||
type stdDispatch struct {
|
||||
lpVtbl *stdDispatchVtbl
|
||||
ref int32
|
||||
iid *ole.GUID
|
||||
iface interface{}
|
||||
funcMap map[string]int32
|
||||
}
|
||||
|
||||
type stdDispatchVtbl struct {
|
||||
pQueryInterface uintptr
|
||||
pAddRef uintptr
|
||||
pRelease uintptr
|
||||
pGetTypeInfoCount uintptr
|
||||
pGetTypeInfo uintptr
|
||||
pGetIDsOfNames uintptr
|
||||
pInvoke uintptr
|
||||
}
|
||||
|
||||
func dispQueryInterface(this *ole.IUnknown, iid *ole.GUID, punk **ole.IUnknown) uint32 {
|
||||
pthis := (*stdDispatch)(unsafe.Pointer(this))
|
||||
*punk = nil
|
||||
if ole.IsEqualGUID(iid, ole.IID_IUnknown) ||
|
||||
ole.IsEqualGUID(iid, ole.IID_IDispatch) {
|
||||
dispAddRef(this)
|
||||
*punk = this
|
||||
return ole.S_OK
|
||||
}
|
||||
if ole.IsEqualGUID(iid, pthis.iid) {
|
||||
dispAddRef(this)
|
||||
*punk = this
|
||||
return ole.S_OK
|
||||
}
|
||||
return ole.E_NOINTERFACE
|
||||
}
|
||||
|
||||
func dispAddRef(this *ole.IUnknown) int32 {
|
||||
pthis := (*stdDispatch)(unsafe.Pointer(this))
|
||||
pthis.ref++
|
||||
return pthis.ref
|
||||
}
|
||||
|
||||
func dispRelease(this *ole.IUnknown) int32 {
|
||||
pthis := (*stdDispatch)(unsafe.Pointer(this))
|
||||
pthis.ref--
|
||||
return pthis.ref
|
||||
}
|
||||
|
||||
func dispGetIDsOfNames(this *ole.IUnknown, iid *ole.GUID, wnames []*uint16, namelen int, lcid int, pdisp []int32) uintptr {
|
||||
pthis := (*stdDispatch)(unsafe.Pointer(this))
|
||||
names := make([]string, len(wnames))
|
||||
for i := 0; i < len(names); i++ {
|
||||
names[i] = ole.LpOleStrToString(wnames[i])
|
||||
}
|
||||
for n := 0; n < namelen; n++ {
|
||||
if id, ok := pthis.funcMap[names[n]]; ok {
|
||||
pdisp[n] = id
|
||||
}
|
||||
}
|
||||
return ole.S_OK
|
||||
}
|
||||
|
||||
func dispGetTypeInfoCount(pcount *int) uintptr {
|
||||
if pcount != nil {
|
||||
*pcount = 0
|
||||
}
|
||||
return ole.S_OK
|
||||
}
|
||||
|
||||
func dispGetTypeInfo(ptypeif *uintptr) uintptr {
|
||||
return ole.E_NOTIMPL
|
||||
}
|
||||
|
||||
func dispInvoke(this *ole.IDispatch, dispid int32, riid *ole.GUID, lcid int, flags int16, dispparams *ole.DISPPARAMS, result *ole.VARIANT, pexcepinfo *ole.EXCEPINFO, nerr *uint) uintptr {
|
||||
pthis := (*stdDispatch)(unsafe.Pointer(this))
|
||||
found := ""
|
||||
for name, id := range pthis.funcMap {
|
||||
if id == dispid {
|
||||
found = name
|
||||
}
|
||||
}
|
||||
if found != "" {
|
||||
rv := reflect.ValueOf(pthis.iface).Elem()
|
||||
rm := rv.MethodByName(found)
|
||||
rr := rm.Call([]reflect.Value{})
|
||||
println(len(rr))
|
||||
return ole.S_OK
|
||||
}
|
||||
return ole.E_NOTIMPL
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package oleutil
|
||||
|
||||
import ole "github.com/gofiber/fiber/v3/internal/go-ole"
|
||||
|
||||
// ConnectObject creates a connection point between two services for communication.
|
||||
func ConnectObject(disp *ole.IDispatch, iid *ole.GUID, idisp interface{}) (uint32, error) {
|
||||
return 0, ole.NewError(ole.E_NOTIMPL)
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package oleutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
ole "github.com/gofiber/fiber/v3/internal/go-ole"
|
||||
)
|
||||
|
||||
// ConnectObject creates a connection point between two services for communication.
|
||||
func ConnectObject(disp *ole.IDispatch, iid *ole.GUID, idisp interface{}) (cookie uint32, err error) {
|
||||
unknown, err := disp.QueryInterface(ole.IID_IConnectionPointContainer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
container := (*ole.IConnectionPointContainer)(unsafe.Pointer(unknown))
|
||||
var point *ole.IConnectionPoint
|
||||
err = container.FindConnectionPoint(iid, &point)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if edisp, ok := idisp.(*ole.IUnknown); ok {
|
||||
cookie, err = point.Advise(edisp)
|
||||
container.Release()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
rv := reflect.ValueOf(disp).Elem()
|
||||
if rv.Type().Kind() == reflect.Struct {
|
||||
dest := &stdDispatch{}
|
||||
dest.lpVtbl = &stdDispatchVtbl{}
|
||||
dest.lpVtbl.pQueryInterface = syscall.NewCallback(dispQueryInterface)
|
||||
dest.lpVtbl.pAddRef = syscall.NewCallback(dispAddRef)
|
||||
dest.lpVtbl.pRelease = syscall.NewCallback(dispRelease)
|
||||
dest.lpVtbl.pGetTypeInfoCount = syscall.NewCallback(dispGetTypeInfoCount)
|
||||
dest.lpVtbl.pGetTypeInfo = syscall.NewCallback(dispGetTypeInfo)
|
||||
dest.lpVtbl.pGetIDsOfNames = syscall.NewCallback(dispGetIDsOfNames)
|
||||
dest.lpVtbl.pInvoke = syscall.NewCallback(dispInvoke)
|
||||
dest.iface = disp
|
||||
dest.iid = iid
|
||||
cookie, err = point.Advise((*ole.IUnknown)(unsafe.Pointer(dest)))
|
||||
container.Release()
|
||||
if err != nil {
|
||||
point.Release()
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
container.Release()
|
||||
|
||||
return 0, ole.NewError(ole.E_INVALIDARG)
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
// This file is here so go get succeeds as without it errors with:
|
||||
// no buildable Go source files in ...
|
||||
//
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package oleutil
|
|
@ -1,127 +0,0 @@
|
|||
package oleutil
|
||||
|
||||
import ole "github.com/gofiber/fiber/v3/internal/go-ole"
|
||||
|
||||
// ClassIDFrom retrieves class ID whether given is program ID or application string.
|
||||
func ClassIDFrom(programID string) (classID *ole.GUID, err error) {
|
||||
return ole.ClassIDFrom(programID)
|
||||
}
|
||||
|
||||
// CreateObject creates object from programID based on interface type.
|
||||
//
|
||||
// Only supports IUnknown.
|
||||
//
|
||||
// Program ID can be either program ID or application string.
|
||||
func CreateObject(programID string) (unknown *ole.IUnknown, err error) {
|
||||
classID, err := ole.ClassIDFrom(programID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
unknown, err = ole.CreateInstance(classID, ole.IID_IUnknown)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetActiveObject retrieves active object for program ID and interface ID based
|
||||
// on interface type.
|
||||
//
|
||||
// Only supports IUnknown.
|
||||
//
|
||||
// Program ID can be either program ID or application string.
|
||||
func GetActiveObject(programID string) (unknown *ole.IUnknown, err error) {
|
||||
classID, err := ole.ClassIDFrom(programID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
unknown, err = ole.GetActiveObject(classID, ole.IID_IUnknown)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CallMethod calls method on IDispatch with parameters.
|
||||
func CallMethod(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) {
|
||||
return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_METHOD, params)
|
||||
}
|
||||
|
||||
// MustCallMethod calls method on IDispatch with parameters or panics.
|
||||
func MustCallMethod(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) {
|
||||
r, err := CallMethod(disp, name, params...)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// GetProperty retrieves property from IDispatch.
|
||||
func GetProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) {
|
||||
return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_PROPERTYGET, params)
|
||||
}
|
||||
|
||||
// MustGetProperty retrieves property from IDispatch or panics.
|
||||
func MustGetProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) {
|
||||
r, err := GetProperty(disp, name, params...)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// PutProperty mutates property.
|
||||
func PutProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) {
|
||||
return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_PROPERTYPUT, params)
|
||||
}
|
||||
|
||||
// MustPutProperty mutates property or panics.
|
||||
func MustPutProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) {
|
||||
r, err := PutProperty(disp, name, params...)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// PutPropertyRef mutates property reference.
|
||||
func PutPropertyRef(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) {
|
||||
return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_PROPERTYPUTREF, params)
|
||||
}
|
||||
|
||||
// MustPutPropertyRef mutates property reference or panics.
|
||||
func MustPutPropertyRef(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) {
|
||||
r, err := PutPropertyRef(disp, name, params...)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func ForEach(disp *ole.IDispatch, f func(v *ole.VARIANT) error) error {
|
||||
newEnum, err := disp.GetProperty("_NewEnum")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer newEnum.Clear()
|
||||
|
||||
enum, err := newEnum.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer enum.Release()
|
||||
|
||||
for item, length, err := enum.Next(1); length > 0; item, length, err = enum.Next(1) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ferr := f(&item); ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// Package is meant to retrieve and process safe array data returned from COM.
|
||||
|
||||
package ole
|
||||
|
||||
// SafeArrayBound defines the SafeArray boundaries.
|
||||
type SafeArrayBound struct {
|
||||
Elements uint32
|
||||
LowerBound int32
|
||||
}
|
||||
|
||||
// SafeArray is how COM handles arrays.
|
||||
type SafeArray struct {
|
||||
Dimensions uint16
|
||||
FeaturesFlag uint16
|
||||
ElementsSize uint32
|
||||
LocksAmount uint32
|
||||
Data uint32
|
||||
Bounds [16]byte
|
||||
}
|
||||
|
||||
// SAFEARRAY is obsolete, exists for backwards compatibility.
|
||||
// Use SafeArray
|
||||
type SAFEARRAY SafeArray
|
||||
|
||||
// SAFEARRAYBOUND is obsolete, exists for backwards compatibility.
|
||||
// Use SafeArrayBound
|
||||
type SAFEARRAYBOUND SafeArrayBound
|
|
@ -1,212 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// safeArrayAccessData returns raw array pointer.
|
||||
//
|
||||
// AKA: SafeArrayAccessData in Windows API.
|
||||
func safeArrayAccessData(safearray *SafeArray) (uintptr, error) {
|
||||
return uintptr(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayUnaccessData releases raw array.
|
||||
//
|
||||
// AKA: SafeArrayUnaccessData in Windows API.
|
||||
func safeArrayUnaccessData(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayAllocData allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocData in Windows API.
|
||||
func safeArrayAllocData(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayAllocDescriptor allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocDescriptor in Windows API.
|
||||
func safeArrayAllocDescriptor(dimensions uint32) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayAllocDescriptorEx allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocDescriptorEx in Windows API.
|
||||
func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCopy returns copy of SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCopy in Windows API.
|
||||
func safeArrayCopy(original *SafeArray) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCopyData duplicates SafeArray into another SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayCopyData in Windows API.
|
||||
func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCreate creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreate in Windows API.
|
||||
func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCreateEx creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateEx in Windows API.
|
||||
func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCreateVector creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateVector in Windows API.
|
||||
func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCreateVectorEx creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateVectorEx in Windows API.
|
||||
func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayDestroy destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroy in Windows API.
|
||||
func safeArrayDestroy(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayDestroyData destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroyData in Windows API.
|
||||
func safeArrayDestroyData(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayDestroyDescriptor destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroyDescriptor in Windows API.
|
||||
func safeArrayDestroyDescriptor(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetDim is the amount of dimensions in the SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetDim in Windows API.
|
||||
func safeArrayGetDim(safearray *SafeArray) (*uint32, error) {
|
||||
u := uint32(0)
|
||||
return &u, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetElementSize is the element size in bytes.
|
||||
//
|
||||
// AKA: SafeArrayGetElemsize in Windows API.
|
||||
func safeArrayGetElementSize(safearray *SafeArray) (*uint32, error) {
|
||||
u := uint32(0)
|
||||
return &u, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetElement retrieves element at given index.
|
||||
func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetElement retrieves element at given index and converts to string.
|
||||
func safeArrayGetElementString(safearray *SafeArray, index int32) (string, error) {
|
||||
return "", NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetIID is the InterfaceID of the elements in the SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayGetIID in Windows API.
|
||||
func safeArrayGetIID(safearray *SafeArray) (*GUID, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetLBound returns lower bounds of SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetLBound in Windows API.
|
||||
func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (int32, error) {
|
||||
return int32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetUBound returns upper bounds of SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetUBound in Windows API.
|
||||
func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (int32, error) {
|
||||
return int32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetVartype returns data type of SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayGetVartype in Windows API.
|
||||
func safeArrayGetVartype(safearray *SafeArray) (uint16, error) {
|
||||
return uint16(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayLock locks SafeArray for reading to modify SafeArray.
|
||||
//
|
||||
// This must be called during some calls to ensure that another process does not
|
||||
// read or write to the SafeArray during editing.
|
||||
//
|
||||
// AKA: SafeArrayLock in Windows API.
|
||||
func safeArrayLock(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayUnlock unlocks SafeArray for reading.
|
||||
//
|
||||
// AKA: SafeArrayUnlock in Windows API.
|
||||
func safeArrayUnlock(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayPutElement stores the data element at the specified location in the
|
||||
// array.
|
||||
//
|
||||
// AKA: SafeArrayPutElement in Windows API.
|
||||
func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetRecordInfo accesses IRecordInfo info for custom types.
|
||||
//
|
||||
// AKA: SafeArrayGetRecordInfo in Windows API.
|
||||
//
|
||||
// XXX: Must implement IRecordInfo interface for this to return.
|
||||
func safeArrayGetRecordInfo(safearray *SafeArray) (interface{}, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArraySetRecordInfo mutates IRecordInfo info for custom types.
|
||||
//
|
||||
// AKA: SafeArraySetRecordInfo in Windows API.
|
||||
//
|
||||
// XXX: Must implement IRecordInfo interface for this to return.
|
||||
func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
|
@ -1,338 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
procSafeArrayAccessData = modoleaut32.NewProc("SafeArrayAccessData")
|
||||
procSafeArrayAllocData = modoleaut32.NewProc("SafeArrayAllocData")
|
||||
procSafeArrayAllocDescriptor = modoleaut32.NewProc("SafeArrayAllocDescriptor")
|
||||
procSafeArrayAllocDescriptorEx = modoleaut32.NewProc("SafeArrayAllocDescriptorEx")
|
||||
procSafeArrayCopy = modoleaut32.NewProc("SafeArrayCopy")
|
||||
procSafeArrayCopyData = modoleaut32.NewProc("SafeArrayCopyData")
|
||||
procSafeArrayCreate = modoleaut32.NewProc("SafeArrayCreate")
|
||||
procSafeArrayCreateEx = modoleaut32.NewProc("SafeArrayCreateEx")
|
||||
procSafeArrayCreateVector = modoleaut32.NewProc("SafeArrayCreateVector")
|
||||
procSafeArrayCreateVectorEx = modoleaut32.NewProc("SafeArrayCreateVectorEx")
|
||||
procSafeArrayDestroy = modoleaut32.NewProc("SafeArrayDestroy")
|
||||
procSafeArrayDestroyData = modoleaut32.NewProc("SafeArrayDestroyData")
|
||||
procSafeArrayDestroyDescriptor = modoleaut32.NewProc("SafeArrayDestroyDescriptor")
|
||||
procSafeArrayGetDim = modoleaut32.NewProc("SafeArrayGetDim")
|
||||
procSafeArrayGetElement = modoleaut32.NewProc("SafeArrayGetElement")
|
||||
procSafeArrayGetElemsize = modoleaut32.NewProc("SafeArrayGetElemsize")
|
||||
procSafeArrayGetIID = modoleaut32.NewProc("SafeArrayGetIID")
|
||||
procSafeArrayGetLBound = modoleaut32.NewProc("SafeArrayGetLBound")
|
||||
procSafeArrayGetUBound = modoleaut32.NewProc("SafeArrayGetUBound")
|
||||
procSafeArrayGetVartype = modoleaut32.NewProc("SafeArrayGetVartype")
|
||||
procSafeArrayLock = modoleaut32.NewProc("SafeArrayLock")
|
||||
procSafeArrayPtrOfIndex = modoleaut32.NewProc("SafeArrayPtrOfIndex")
|
||||
procSafeArrayUnaccessData = modoleaut32.NewProc("SafeArrayUnaccessData")
|
||||
procSafeArrayUnlock = modoleaut32.NewProc("SafeArrayUnlock")
|
||||
procSafeArrayPutElement = modoleaut32.NewProc("SafeArrayPutElement")
|
||||
//procSafeArrayRedim = modoleaut32.NewProc("SafeArrayRedim") // TODO
|
||||
//procSafeArraySetIID = modoleaut32.NewProc("SafeArraySetIID") // TODO
|
||||
procSafeArrayGetRecordInfo = modoleaut32.NewProc("SafeArrayGetRecordInfo")
|
||||
procSafeArraySetRecordInfo = modoleaut32.NewProc("SafeArraySetRecordInfo")
|
||||
)
|
||||
|
||||
// safeArrayAccessData returns raw array pointer.
|
||||
//
|
||||
// AKA: SafeArrayAccessData in Windows API.
|
||||
// Todo: Test
|
||||
func safeArrayAccessData(safearray *SafeArray) (element uintptr, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayAccessData.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&element))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayUnaccessData releases raw array.
|
||||
//
|
||||
// AKA: SafeArrayUnaccessData in Windows API.
|
||||
func safeArrayUnaccessData(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayUnaccessData.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayAllocData allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocData in Windows API.
|
||||
func safeArrayAllocData(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayAllocData.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayAllocDescriptor allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocDescriptor in Windows API.
|
||||
func safeArrayAllocDescriptor(dimensions uint32) (safearray *SafeArray, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayAllocDescriptor.Call(uintptr(dimensions), uintptr(unsafe.Pointer(&safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayAllocDescriptorEx allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocDescriptorEx in Windows API.
|
||||
func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (safearray *SafeArray, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayAllocDescriptorEx.Call(
|
||||
uintptr(variantType),
|
||||
uintptr(dimensions),
|
||||
uintptr(unsafe.Pointer(&safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCopy returns copy of SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCopy in Windows API.
|
||||
func safeArrayCopy(original *SafeArray) (safearray *SafeArray, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayCopy.Call(
|
||||
uintptr(unsafe.Pointer(original)),
|
||||
uintptr(unsafe.Pointer(&safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCopyData duplicates SafeArray into another SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayCopyData in Windows API.
|
||||
func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) (err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayCopyData.Call(
|
||||
uintptr(unsafe.Pointer(original)),
|
||||
uintptr(unsafe.Pointer(duplicate))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCreate creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreate in Windows API.
|
||||
func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (safearray *SafeArray, err error) {
|
||||
sa, _, err := procSafeArrayCreate.Call(
|
||||
uintptr(variantType),
|
||||
uintptr(dimensions),
|
||||
uintptr(unsafe.Pointer(bounds)))
|
||||
safearray = (*SafeArray)(unsafe.Pointer(&sa))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCreateEx creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateEx in Windows API.
|
||||
func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (safearray *SafeArray, err error) {
|
||||
sa, _, err := procSafeArrayCreateEx.Call(
|
||||
uintptr(variantType),
|
||||
uintptr(dimensions),
|
||||
uintptr(unsafe.Pointer(bounds)),
|
||||
extra)
|
||||
safearray = (*SafeArray)(unsafe.Pointer(sa))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCreateVector creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateVector in Windows API.
|
||||
func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (safearray *SafeArray, err error) {
|
||||
sa, _, err := procSafeArrayCreateVector.Call(
|
||||
uintptr(variantType),
|
||||
uintptr(lowerBound),
|
||||
uintptr(length))
|
||||
safearray = (*SafeArray)(unsafe.Pointer(sa))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCreateVectorEx creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateVectorEx in Windows API.
|
||||
func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (safearray *SafeArray, err error) {
|
||||
sa, _, err := procSafeArrayCreateVectorEx.Call(
|
||||
uintptr(variantType),
|
||||
uintptr(lowerBound),
|
||||
uintptr(length),
|
||||
extra)
|
||||
safearray = (*SafeArray)(unsafe.Pointer(sa))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayDestroy destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroy in Windows API.
|
||||
func safeArrayDestroy(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayDestroy.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayDestroyData destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroyData in Windows API.
|
||||
func safeArrayDestroyData(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayDestroyData.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayDestroyDescriptor destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroyDescriptor in Windows API.
|
||||
func safeArrayDestroyDescriptor(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayDestroyDescriptor.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetDim is the amount of dimensions in the SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetDim in Windows API.
|
||||
func safeArrayGetDim(safearray *SafeArray) (dimensions *uint32, err error) {
|
||||
l, _, err := procSafeArrayGetDim.Call(uintptr(unsafe.Pointer(safearray)))
|
||||
dimensions = (*uint32)(unsafe.Pointer(l))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetElementSize is the element size in bytes.
|
||||
//
|
||||
// AKA: SafeArrayGetElemsize in Windows API.
|
||||
func safeArrayGetElementSize(safearray *SafeArray) (length *uint32, err error) {
|
||||
l, _, err := procSafeArrayGetElemsize.Call(uintptr(unsafe.Pointer(safearray)))
|
||||
length = (*uint32)(unsafe.Pointer(l))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetElement retrieves element at given index.
|
||||
func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error {
|
||||
return convertHresultToError(
|
||||
procSafeArrayGetElement.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&index)),
|
||||
uintptr(pv)))
|
||||
}
|
||||
|
||||
// safeArrayGetElementString retrieves element at given index and converts to string.
|
||||
func safeArrayGetElementString(safearray *SafeArray, index int32) (str string, err error) {
|
||||
var element *int16
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetElement.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&index)),
|
||||
uintptr(unsafe.Pointer(&element))))
|
||||
str = BstrToString(*(**uint16)(unsafe.Pointer(&element)))
|
||||
SysFreeString(element)
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetIID is the InterfaceID of the elements in the SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayGetIID in Windows API.
|
||||
func safeArrayGetIID(safearray *SafeArray) (guid *GUID, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetIID.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&guid))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetLBound returns lower bounds of SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetLBound in Windows API.
|
||||
func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (lowerBound int32, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetLBound.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(dimension),
|
||||
uintptr(unsafe.Pointer(&lowerBound))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetUBound returns upper bounds of SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetUBound in Windows API.
|
||||
func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (upperBound int32, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetUBound.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(dimension),
|
||||
uintptr(unsafe.Pointer(&upperBound))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetVartype returns data type of SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayGetVartype in Windows API.
|
||||
func safeArrayGetVartype(safearray *SafeArray) (varType uint16, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetVartype.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&varType))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayLock locks SafeArray for reading to modify SafeArray.
|
||||
//
|
||||
// This must be called during some calls to ensure that another process does not
|
||||
// read or write to the SafeArray during editing.
|
||||
//
|
||||
// AKA: SafeArrayLock in Windows API.
|
||||
func safeArrayLock(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayLock.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayUnlock unlocks SafeArray for reading.
|
||||
//
|
||||
// AKA: SafeArrayUnlock in Windows API.
|
||||
func safeArrayUnlock(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayUnlock.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayPutElement stores the data element at the specified location in the
|
||||
// array.
|
||||
//
|
||||
// AKA: SafeArrayPutElement in Windows API.
|
||||
func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) (err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayPutElement.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&index)),
|
||||
uintptr(unsafe.Pointer(element))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetRecordInfo accesses IRecordInfo info for custom types.
|
||||
//
|
||||
// AKA: SafeArrayGetRecordInfo in Windows API.
|
||||
//
|
||||
// XXX: Must implement IRecordInfo interface for this to return.
|
||||
func safeArrayGetRecordInfo(safearray *SafeArray) (recordInfo interface{}, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetRecordInfo.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&recordInfo))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArraySetRecordInfo mutates IRecordInfo info for custom types.
|
||||
//
|
||||
// AKA: SafeArraySetRecordInfo in Windows API.
|
||||
//
|
||||
// XXX: Must implement IRecordInfo interface for this to return.
|
||||
func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) (err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArraySetRecordInfo.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&recordInfo))))
|
||||
return
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
// Helper for converting SafeArray to array of objects.
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type SafeArrayConversion struct {
|
||||
Array *SafeArray
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) ToStringArray() (strings []string) {
|
||||
totalElements, _ := sac.TotalElements(0)
|
||||
strings = make([]string, totalElements)
|
||||
|
||||
for i := int32(0); i < totalElements; i++ {
|
||||
strings[int32(i)], _ = safeArrayGetElementString(sac.Array, i)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) ToByteArray() (bytes []byte) {
|
||||
totalElements, _ := sac.TotalElements(0)
|
||||
bytes = make([]byte, totalElements)
|
||||
|
||||
for i := int32(0); i < totalElements; i++ {
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&bytes[int32(i)]))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) ToValueArray() (values []interface{}) {
|
||||
totalElements, _ := sac.TotalElements(0)
|
||||
values = make([]interface{}, totalElements)
|
||||
vt, _ := safeArrayGetVartype(sac.Array)
|
||||
|
||||
for i := int32(0); i < totalElements; i++ {
|
||||
switch VT(vt) {
|
||||
case VT_BOOL:
|
||||
var v bool
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_I1:
|
||||
var v int8
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_I2:
|
||||
var v int16
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_I4:
|
||||
var v int32
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_I8:
|
||||
var v int64
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_UI1:
|
||||
var v uint8
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_UI2:
|
||||
var v uint16
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_UI4:
|
||||
var v uint32
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_UI8:
|
||||
var v uint64
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_R4:
|
||||
var v float32
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_R8:
|
||||
var v float64
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_BSTR:
|
||||
var v string
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_VARIANT:
|
||||
var v VARIANT
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v.Value()
|
||||
default:
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) GetType() (varType uint16, err error) {
|
||||
return safeArrayGetVartype(sac.Array)
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) GetDimensions() (dimensions *uint32, err error) {
|
||||
return safeArrayGetDim(sac.Array)
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) GetSize() (length *uint32, err error) {
|
||||
return safeArrayGetElementSize(sac.Array)
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) TotalElements(index uint32) (totalElements int32, err error) {
|
||||
if index < 1 {
|
||||
index = 1
|
||||
}
|
||||
|
||||
// Get array bounds
|
||||
var LowerBounds int32
|
||||
var UpperBounds int32
|
||||
|
||||
LowerBounds, err = safeArrayGetLBound(sac.Array, index)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
UpperBounds, err = safeArrayGetUBound(sac.Array, index)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
totalElements = UpperBounds - LowerBounds + 1
|
||||
return
|
||||
}
|
||||
|
||||
// Release Safe Array memory
|
||||
func (sac *SafeArrayConversion) Release() {
|
||||
safeArrayDestroy(sac.Array)
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func safeArrayFromByteSlice(slice []byte) *SafeArray {
|
||||
array, _ := safeArrayCreateVector(VT_UI1, 0, uint32(len(slice)))
|
||||
|
||||
if array == nil {
|
||||
panic("Could not convert []byte to SAFEARRAY")
|
||||
}
|
||||
|
||||
for i, v := range slice {
|
||||
safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(&v)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
func safeArrayFromStringSlice(slice []string) *SafeArray {
|
||||
array, _ := safeArrayCreateVector(VT_BSTR, 0, uint32(len(slice)))
|
||||
|
||||
if array == nil {
|
||||
panic("Could not convert []string to SAFEARRAY")
|
||||
}
|
||||
// SysAllocStringLen(s)
|
||||
for i, v := range slice {
|
||||
safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(SysAllocStringLen(v))))
|
||||
}
|
||||
return array
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
package ole
|
||||
|
||||
import (
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ClassIDFrom retrieves class ID whether given is program ID or application string.
|
||||
//
|
||||
// Helper that provides check against both Class ID from Program ID and Class ID from string. It is
|
||||
// faster, if you know which you are using, to use the individual functions, but this will check
|
||||
// against available functions for you.
|
||||
func ClassIDFrom(programID string) (classID *GUID, err error) {
|
||||
classID, err = CLSIDFromProgID(programID)
|
||||
if err != nil {
|
||||
classID, err = CLSIDFromString(programID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BytePtrToString converts byte pointer to a Go string.
|
||||
func BytePtrToString(p *byte) string {
|
||||
a := (*[10000]uint8)(unsafe.Pointer(p))
|
||||
i := 0
|
||||
for a[i] != 0 {
|
||||
i++
|
||||
}
|
||||
return string(a[:i])
|
||||
}
|
||||
|
||||
// UTF16PtrToString is alias for LpOleStrToString.
|
||||
//
|
||||
// Kept for compatibility reasons.
|
||||
func UTF16PtrToString(p *uint16) string {
|
||||
return LpOleStrToString(p)
|
||||
}
|
||||
|
||||
// LpOleStrToString converts COM Unicode to Go string.
|
||||
func LpOleStrToString(p *uint16) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
length := lpOleStrLen(p)
|
||||
a := make([]uint16, length)
|
||||
|
||||
ptr := unsafe.Pointer(p)
|
||||
|
||||
for i := 0; i < int(length); i++ {
|
||||
a[i] = *(*uint16)(ptr)
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + 2)
|
||||
}
|
||||
|
||||
return string(utf16.Decode(a))
|
||||
}
|
||||
|
||||
// BstrToString converts COM binary string to Go string.
|
||||
func BstrToString(p *uint16) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
length := SysStringLen((*int16)(unsafe.Pointer(p)))
|
||||
a := make([]uint16, length)
|
||||
|
||||
ptr := unsafe.Pointer(p)
|
||||
|
||||
for i := 0; i < int(length); i++ {
|
||||
a[i] = *(*uint16)(ptr)
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + 2)
|
||||
}
|
||||
return string(utf16.Decode(a))
|
||||
}
|
||||
|
||||
// lpOleStrLen returns the length of Unicode string.
|
||||
func lpOleStrLen(p *uint16) (length int64) {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
ptr := unsafe.Pointer(p)
|
||||
|
||||
for i := 0; ; i++ {
|
||||
if 0 == *(*uint16)(ptr) {
|
||||
length = int64(i)
|
||||
break
|
||||
}
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + 2)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// convertHresultToError converts syscall to error, if call is unsuccessful.
|
||||
func convertHresultToError(hr uintptr, r2 uintptr, ignore error) (err error) {
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
modcombase = windows.NewLazySystemDLL("combase.dll")
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
modole32 = windows.NewLazySystemDLL("ole32.dll")
|
||||
modoleaut32 = windows.NewLazySystemDLL("oleaut32.dll")
|
||||
moduser32 = windows.NewLazySystemDLL("user32.dll")
|
||||
)
|
|
@ -1,105 +0,0 @@
|
|||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// NewVariant returns new variant based on type and value.
|
||||
func NewVariant(vt VT, val int64) VARIANT {
|
||||
return VARIANT{VT: vt, Val: val}
|
||||
}
|
||||
|
||||
// ToIUnknown converts Variant to Unknown object.
|
||||
func (v *VARIANT) ToIUnknown() *IUnknown {
|
||||
if v.VT != VT_UNKNOWN {
|
||||
return nil
|
||||
}
|
||||
return (*IUnknown)(unsafe.Pointer(uintptr(v.Val)))
|
||||
}
|
||||
|
||||
// ToIDispatch converts variant to dispatch object.
|
||||
func (v *VARIANT) ToIDispatch() *IDispatch {
|
||||
if v.VT != VT_DISPATCH {
|
||||
return nil
|
||||
}
|
||||
return (*IDispatch)(unsafe.Pointer(uintptr(v.Val)))
|
||||
}
|
||||
|
||||
// ToArray converts variant to SafeArray helper.
|
||||
func (v *VARIANT) ToArray() *SafeArrayConversion {
|
||||
if v.VT != VT_SAFEARRAY {
|
||||
if v.VT&VT_ARRAY == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var safeArray *SafeArray = (*SafeArray)(unsafe.Pointer(uintptr(v.Val)))
|
||||
return &SafeArrayConversion{safeArray}
|
||||
}
|
||||
|
||||
// ToString converts variant to Go string.
|
||||
func (v *VARIANT) ToString() string {
|
||||
if v.VT != VT_BSTR {
|
||||
return ""
|
||||
}
|
||||
return BstrToString(*(**uint16)(unsafe.Pointer(&v.Val)))
|
||||
}
|
||||
|
||||
// Clear the memory of variant object.
|
||||
func (v *VARIANT) Clear() error {
|
||||
return VariantClear(v)
|
||||
}
|
||||
|
||||
// Value returns variant value based on its type.
|
||||
//
|
||||
// Currently supported types: 2- and 4-byte integers, strings, bools.
|
||||
// Note that 64-bit integers, datetimes, and other types are stored as strings
|
||||
// and will be returned as strings.
|
||||
//
|
||||
// Needs to be further converted, because this returns an interface{}.
|
||||
func (v *VARIANT) Value() interface{} {
|
||||
switch v.VT {
|
||||
case VT_I1:
|
||||
return int8(v.Val)
|
||||
case VT_UI1:
|
||||
return uint8(v.Val)
|
||||
case VT_I2:
|
||||
return int16(v.Val)
|
||||
case VT_UI2:
|
||||
return uint16(v.Val)
|
||||
case VT_I4:
|
||||
return int32(v.Val)
|
||||
case VT_UI4:
|
||||
return uint32(v.Val)
|
||||
case VT_I8:
|
||||
return int64(v.Val)
|
||||
case VT_UI8:
|
||||
return uint64(v.Val)
|
||||
case VT_INT:
|
||||
return int(v.Val)
|
||||
case VT_UINT:
|
||||
return uint(v.Val)
|
||||
case VT_INT_PTR:
|
||||
return uintptr(v.Val) // TODO
|
||||
case VT_UINT_PTR:
|
||||
return uintptr(v.Val)
|
||||
case VT_R4:
|
||||
return *(*float32)(unsafe.Pointer(&v.Val))
|
||||
case VT_R8:
|
||||
return *(*float64)(unsafe.Pointer(&v.Val))
|
||||
case VT_BSTR:
|
||||
return v.ToString()
|
||||
case VT_DATE:
|
||||
// VT_DATE type will either return float64 or time.Time.
|
||||
d := uint64(v.Val)
|
||||
date, err := GetVariantDate(d)
|
||||
if err != nil {
|
||||
return float64(v.Val)
|
||||
}
|
||||
return date
|
||||
case VT_UNKNOWN:
|
||||
return v.ToIUnknown()
|
||||
case VT_DISPATCH:
|
||||
return v.ToIDispatch()
|
||||
case VT_BOOL:
|
||||
return v.Val != 0
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
//go:build 386
|
||||
// +build 386
|
||||
|
||||
package ole
|
||||
|
||||
type VARIANT struct {
|
||||
VT VT // 2
|
||||
wReserved1 uint16 // 4
|
||||
wReserved2 uint16 // 6
|
||||
wReserved3 uint16 // 8
|
||||
Val int64 // 16
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
//go:build amd64
|
||||
// +build amd64
|
||||
|
||||
package ole
|
||||
|
||||
type VARIANT struct {
|
||||
VT VT // 2
|
||||
wReserved1 uint16 // 4
|
||||
wReserved2 uint16 // 6
|
||||
wReserved3 uint16 // 8
|
||||
Val int64 // 16
|
||||
_ [8]byte // 24
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
//go:build windows && 386
|
||||
// +build windows,386
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetVariantDate converts COM Variant Time value to Go time.Time.
|
||||
func GetVariantDate(value uint64) (time.Time, error) {
|
||||
var st syscall.Systemtime
|
||||
v1 := uint32(value)
|
||||
v2 := uint32(value >> 32)
|
||||
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st)))
|
||||
if r != 0 {
|
||||
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
|
||||
}
|
||||
return time.Now(), errors.New("Could not convert to time, passing current time.")
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
//go:build windows && amd64
|
||||
// +build windows,amd64
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetVariantDate converts COM Variant Time value to Go time.Time.
|
||||
func GetVariantDate(value uint64) (time.Time, error) {
|
||||
var st syscall.Systemtime
|
||||
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st)))
|
||||
if r != 0 {
|
||||
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
|
||||
}
|
||||
return time.Now(), errors.New("Could not convert to time, passing current time.")
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
//go:build ppc64le
|
||||
// +build ppc64le
|
||||
|
||||
package ole
|
||||
|
||||
type VARIANT struct {
|
||||
VT VT // 2
|
||||
wReserved1 uint16 // 4
|
||||
wReserved2 uint16 // 6
|
||||
wReserved3 uint16 // 8
|
||||
Val int64 // 16
|
||||
_ [8]byte // 24
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
//go:build s390x
|
||||
// +build s390x
|
||||
|
||||
package ole
|
||||
|
||||
type VARIANT struct {
|
||||
VT VT // 2
|
||||
wReserved1 uint16 // 4
|
||||
wReserved2 uint16 // 6
|
||||
wReserved3 uint16 // 8
|
||||
Val int64 // 16
|
||||
_ [8]byte // 24
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
// generated by stringer -output vt_string.go -type VT; DO NOT EDIT
|
||||
|
||||
package ole
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
_VT_name_0 = "VT_EMPTYVT_NULLVT_I2VT_I4VT_R4VT_R8VT_CYVT_DATEVT_BSTRVT_DISPATCHVT_ERRORVT_BOOLVT_VARIANTVT_UNKNOWNVT_DECIMAL"
|
||||
_VT_name_1 = "VT_I1VT_UI1VT_UI2VT_UI4VT_I8VT_UI8VT_INTVT_UINTVT_VOIDVT_HRESULTVT_PTRVT_SAFEARRAYVT_CARRAYVT_USERDEFINEDVT_LPSTRVT_LPWSTR"
|
||||
_VT_name_2 = "VT_RECORDVT_INT_PTRVT_UINT_PTR"
|
||||
_VT_name_3 = "VT_FILETIMEVT_BLOBVT_STREAMVT_STORAGEVT_STREAMED_OBJECTVT_STORED_OBJECTVT_BLOB_OBJECTVT_CFVT_CLSID"
|
||||
_VT_name_4 = "VT_BSTR_BLOBVT_VECTOR"
|
||||
_VT_name_5 = "VT_ARRAY"
|
||||
_VT_name_6 = "VT_BYREF"
|
||||
_VT_name_7 = "VT_RESERVED"
|
||||
_VT_name_8 = "VT_ILLEGAL"
|
||||
)
|
||||
|
||||
var (
|
||||
_VT_index_0 = [...]uint8{0, 8, 15, 20, 25, 30, 35, 40, 47, 54, 65, 73, 80, 90, 100, 110}
|
||||
_VT_index_1 = [...]uint8{0, 5, 11, 17, 23, 28, 34, 40, 47, 54, 64, 70, 82, 91, 105, 113, 122}
|
||||
_VT_index_2 = [...]uint8{0, 9, 19, 30}
|
||||
_VT_index_3 = [...]uint8{0, 11, 18, 27, 37, 55, 71, 85, 90, 98}
|
||||
_VT_index_4 = [...]uint8{0, 12, 21}
|
||||
_VT_index_5 = [...]uint8{0, 8}
|
||||
_VT_index_6 = [...]uint8{0, 8}
|
||||
_VT_index_7 = [...]uint8{0, 11}
|
||||
_VT_index_8 = [...]uint8{0, 10}
|
||||
)
|
||||
|
||||
func (i VT) String() string {
|
||||
switch {
|
||||
case 0 <= i && i <= 14:
|
||||
return _VT_name_0[_VT_index_0[i]:_VT_index_0[i+1]]
|
||||
case 16 <= i && i <= 31:
|
||||
i -= 16
|
||||
return _VT_name_1[_VT_index_1[i]:_VT_index_1[i+1]]
|
||||
case 36 <= i && i <= 38:
|
||||
i -= 36
|
||||
return _VT_name_2[_VT_index_2[i]:_VT_index_2[i+1]]
|
||||
case 64 <= i && i <= 72:
|
||||
i -= 64
|
||||
return _VT_name_3[_VT_index_3[i]:_VT_index_3[i+1]]
|
||||
case 4095 <= i && i <= 4096:
|
||||
i -= 4095
|
||||
return _VT_name_4[_VT_index_4[i]:_VT_index_4[i+1]]
|
||||
case i == 8192:
|
||||
return _VT_name_5
|
||||
case i == 16384:
|
||||
return _VT_name_6
|
||||
case i == 32768:
|
||||
return _VT_name_7
|
||||
case i == 65535:
|
||||
return _VT_name_8
|
||||
default:
|
||||
return fmt.Sprintf("VT(%d)", i)
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
procRoInitialize = modcombase.NewProc("RoInitialize")
|
||||
procRoActivateInstance = modcombase.NewProc("RoActivateInstance")
|
||||
procRoGetActivationFactory = modcombase.NewProc("RoGetActivationFactory")
|
||||
procWindowsCreateString = modcombase.NewProc("WindowsCreateString")
|
||||
procWindowsDeleteString = modcombase.NewProc("WindowsDeleteString")
|
||||
procWindowsGetStringRawBuffer = modcombase.NewProc("WindowsGetStringRawBuffer")
|
||||
)
|
||||
|
||||
func RoInitialize(thread_type uint32) (err error) {
|
||||
hr, _, _ := procRoInitialize.Call(uintptr(thread_type))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func RoActivateInstance(clsid string) (ins *IInspectable, err error) {
|
||||
hClsid, err := NewHString(clsid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer DeleteHString(hClsid)
|
||||
|
||||
hr, _, _ := procRoActivateInstance.Call(
|
||||
uintptr(unsafe.Pointer(hClsid)),
|
||||
uintptr(unsafe.Pointer(&ins)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func RoGetActivationFactory(clsid string, iid *GUID) (ins *IInspectable, err error) {
|
||||
hClsid, err := NewHString(clsid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer DeleteHString(hClsid)
|
||||
|
||||
hr, _, _ := procRoGetActivationFactory.Call(
|
||||
uintptr(unsafe.Pointer(hClsid)),
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(&ins)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HString is handle string for pointers.
|
||||
type HString uintptr
|
||||
|
||||
// NewHString returns a new HString for Go string.
|
||||
func NewHString(s string) (hstring HString, err error) {
|
||||
u16 := syscall.StringToUTF16Ptr(s)
|
||||
len := uint32(utf8.RuneCountInString(s))
|
||||
hr, _, _ := procWindowsCreateString.Call(
|
||||
uintptr(unsafe.Pointer(u16)),
|
||||
uintptr(len),
|
||||
uintptr(unsafe.Pointer(&hstring)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteHString deletes HString.
|
||||
func DeleteHString(hstring HString) (err error) {
|
||||
hr, _, _ := procWindowsDeleteString.Call(uintptr(hstring))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// String returns Go string value of HString.
|
||||
func (h HString) String() string {
|
||||
var u16buf uintptr
|
||||
var u16len uint32
|
||||
u16buf, _, _ = procWindowsGetStringRawBuffer.Call(
|
||||
uintptr(h),
|
||||
uintptr(unsafe.Pointer(&u16len)))
|
||||
|
||||
u16hdr := reflect.SliceHeader{Data: u16buf, Len: int(u16len), Cap: int(u16len)}
|
||||
u16 := *(*[]uint16)(unsafe.Pointer(&u16hdr))
|
||||
return syscall.UTF16ToString(u16)
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
// RoInitialize
|
||||
func RoInitialize(thread_type uint32) (err error) {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// RoActivateInstance
|
||||
func RoActivateInstance(clsid string) (ins *IInspectable, err error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// RoGetActivationFactory
|
||||
func RoGetActivationFactory(clsid string, iid *GUID) (ins *IInspectable, err error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// HString is handle string for pointers.
|
||||
type HString uintptr
|
||||
|
||||
// NewHString returns a new HString for Go string.
|
||||
func NewHString(s string) (hstring HString, err error) {
|
||||
return HString(uintptr(0)), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// DeleteHString deletes HString.
|
||||
func DeleteHString(hstring HString) (err error) {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// String returns Go string value of HString.
|
||||
func (h HString) String() string {
|
||||
return ""
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
gopsutil is distributed under BSD license reproduced below.
|
||||
|
||||
Copyright (c) 2014, WAKAYAMA Shirou
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of the gopsutil authors nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
-------
|
||||
internal/common/binary.go in the gopsutil is copied and modifid from golang/encoding/binary.go.
|
||||
|
||||
|
||||
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,634 +0,0 @@
|
|||
package common
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package binary implements simple translation between numbers and byte
|
||||
// sequences and encoding and decoding of varints.
|
||||
//
|
||||
// Numbers are translated by reading and writing fixed-size values.
|
||||
// A fixed-size value is either a fixed-size arithmetic
|
||||
// type (int8, uint8, int16, float32, complex64, ...)
|
||||
// or an array or struct containing only fixed-size values.
|
||||
//
|
||||
// The varint functions encode and decode single integer values using
|
||||
// a variable-length encoding; smaller values require fewer bytes.
|
||||
// For a specification, see
|
||||
// http://code.google.com/apis/protocolbuffers/docs/encoding.html.
|
||||
//
|
||||
// This package favors simplicity over efficiency. Clients that require
|
||||
// high-performance serialization, especially for large data structures,
|
||||
// should look at more advanced solutions such as the encoding/gob
|
||||
// package or protocol buffers.
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// A ByteOrder specifies how to convert byte sequences into
|
||||
// 16-, 32-, or 64-bit unsigned integers.
|
||||
type ByteOrder interface {
|
||||
Uint16([]byte) uint16
|
||||
Uint32([]byte) uint32
|
||||
Uint64([]byte) uint64
|
||||
PutUint16([]byte, uint16)
|
||||
PutUint32([]byte, uint32)
|
||||
PutUint64([]byte, uint64)
|
||||
String() string
|
||||
}
|
||||
|
||||
// LittleEndian is the little-endian implementation of ByteOrder.
|
||||
var LittleEndian littleEndian
|
||||
|
||||
// BigEndian is the big-endian implementation of ByteOrder.
|
||||
var BigEndian bigEndian
|
||||
|
||||
type littleEndian struct{}
|
||||
|
||||
func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 }
|
||||
|
||||
func (littleEndian) PutUint16(b []byte, v uint16) {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
}
|
||||
|
||||
func (littleEndian) Uint32(b []byte) uint32 {
|
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
}
|
||||
|
||||
func (littleEndian) PutUint32(b []byte, v uint32) {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
}
|
||||
|
||||
func (littleEndian) Uint64(b []byte) uint64 {
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
||||
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
||||
}
|
||||
|
||||
func (littleEndian) PutUint64(b []byte, v uint64) {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
b[4] = byte(v >> 32)
|
||||
b[5] = byte(v >> 40)
|
||||
b[6] = byte(v >> 48)
|
||||
b[7] = byte(v >> 56)
|
||||
}
|
||||
|
||||
func (littleEndian) String() string { return "LittleEndian" }
|
||||
|
||||
func (littleEndian) GoString() string { return "binary.LittleEndian" }
|
||||
|
||||
type bigEndian struct{}
|
||||
|
||||
func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 }
|
||||
|
||||
func (bigEndian) PutUint16(b []byte, v uint16) {
|
||||
b[0] = byte(v >> 8)
|
||||
b[1] = byte(v)
|
||||
}
|
||||
|
||||
func (bigEndian) Uint32(b []byte) uint32 {
|
||||
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
|
||||
}
|
||||
|
||||
func (bigEndian) PutUint32(b []byte, v uint32) {
|
||||
b[0] = byte(v >> 24)
|
||||
b[1] = byte(v >> 16)
|
||||
b[2] = byte(v >> 8)
|
||||
b[3] = byte(v)
|
||||
}
|
||||
|
||||
func (bigEndian) Uint64(b []byte) uint64 {
|
||||
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
||||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||||
}
|
||||
|
||||
func (bigEndian) PutUint64(b []byte, v uint64) {
|
||||
b[0] = byte(v >> 56)
|
||||
b[1] = byte(v >> 48)
|
||||
b[2] = byte(v >> 40)
|
||||
b[3] = byte(v >> 32)
|
||||
b[4] = byte(v >> 24)
|
||||
b[5] = byte(v >> 16)
|
||||
b[6] = byte(v >> 8)
|
||||
b[7] = byte(v)
|
||||
}
|
||||
|
||||
func (bigEndian) String() string { return "BigEndian" }
|
||||
|
||||
func (bigEndian) GoString() string { return "binary.BigEndian" }
|
||||
|
||||
// Read reads structured binary data from r into data.
|
||||
// Data must be a pointer to a fixed-size value or a slice
|
||||
// of fixed-size values.
|
||||
// Bytes read from r are decoded using the specified byte order
|
||||
// and written to successive fields of the data.
|
||||
// When reading into structs, the field data for fields with
|
||||
// blank (_) field names is skipped; i.e., blank field names
|
||||
// may be used for padding.
|
||||
// When reading into a struct, all non-blank fields must be exported.
|
||||
func Read(r io.Reader, order ByteOrder, data interface{}) error {
|
||||
// Fast path for basic types and slices.
|
||||
if n := intDataSize(data); n != 0 {
|
||||
var b [8]byte
|
||||
var bs []byte
|
||||
if n > len(b) {
|
||||
bs = make([]byte, n)
|
||||
} else {
|
||||
bs = b[:n]
|
||||
}
|
||||
if _, err := io.ReadFull(r, bs); err != nil {
|
||||
return err
|
||||
}
|
||||
switch data := data.(type) {
|
||||
case *int8:
|
||||
*data = int8(b[0])
|
||||
case *uint8:
|
||||
*data = b[0]
|
||||
case *int16:
|
||||
*data = int16(order.Uint16(bs))
|
||||
case *uint16:
|
||||
*data = order.Uint16(bs)
|
||||
case *int32:
|
||||
*data = int32(order.Uint32(bs))
|
||||
case *uint32:
|
||||
*data = order.Uint32(bs)
|
||||
case *int64:
|
||||
*data = int64(order.Uint64(bs))
|
||||
case *uint64:
|
||||
*data = order.Uint64(bs)
|
||||
case []int8:
|
||||
for i, x := range bs { // Easier to loop over the input for 8-bit values.
|
||||
data[i] = int8(x)
|
||||
}
|
||||
case []uint8:
|
||||
copy(data, bs)
|
||||
case []int16:
|
||||
for i := range data {
|
||||
data[i] = int16(order.Uint16(bs[2*i:]))
|
||||
}
|
||||
case []uint16:
|
||||
for i := range data {
|
||||
data[i] = order.Uint16(bs[2*i:])
|
||||
}
|
||||
case []int32:
|
||||
for i := range data {
|
||||
data[i] = int32(order.Uint32(bs[4*i:]))
|
||||
}
|
||||
case []uint32:
|
||||
for i := range data {
|
||||
data[i] = order.Uint32(bs[4*i:])
|
||||
}
|
||||
case []int64:
|
||||
for i := range data {
|
||||
data[i] = int64(order.Uint64(bs[8*i:]))
|
||||
}
|
||||
case []uint64:
|
||||
for i := range data {
|
||||
data[i] = order.Uint64(bs[8*i:])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fallback to reflect-based decoding.
|
||||
v := reflect.ValueOf(data)
|
||||
size := -1
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr:
|
||||
v = v.Elem()
|
||||
size = dataSize(v)
|
||||
case reflect.Slice:
|
||||
size = dataSize(v)
|
||||
}
|
||||
if size < 0 {
|
||||
return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String())
|
||||
}
|
||||
d := &decoder{order: order, buf: make([]byte, size)}
|
||||
if _, err := io.ReadFull(r, d.buf); err != nil {
|
||||
return err
|
||||
}
|
||||
d.value(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes the binary representation of data into w.
|
||||
// Data must be a fixed-size value or a slice of fixed-size
|
||||
// values, or a pointer to such data.
|
||||
// Bytes written to w are encoded using the specified byte order
|
||||
// and read from successive fields of the data.
|
||||
// When writing structs, zero values are written for fields
|
||||
// with blank (_) field names.
|
||||
func Write(w io.Writer, order ByteOrder, data interface{}) error {
|
||||
// Fast path for basic types and slices.
|
||||
if n := intDataSize(data); n != 0 {
|
||||
var b [8]byte
|
||||
var bs []byte
|
||||
if n > len(b) {
|
||||
bs = make([]byte, n)
|
||||
} else {
|
||||
bs = b[:n]
|
||||
}
|
||||
switch v := data.(type) {
|
||||
case *int8:
|
||||
bs = b[:1]
|
||||
b[0] = byte(*v)
|
||||
case int8:
|
||||
bs = b[:1]
|
||||
b[0] = byte(v)
|
||||
case []int8:
|
||||
for i, x := range v {
|
||||
bs[i] = byte(x)
|
||||
}
|
||||
case *uint8:
|
||||
bs = b[:1]
|
||||
b[0] = *v
|
||||
case uint8:
|
||||
bs = b[:1]
|
||||
b[0] = byte(v)
|
||||
case []uint8:
|
||||
bs = v
|
||||
case *int16:
|
||||
bs = b[:2]
|
||||
order.PutUint16(bs, uint16(*v))
|
||||
case int16:
|
||||
bs = b[:2]
|
||||
order.PutUint16(bs, uint16(v))
|
||||
case []int16:
|
||||
for i, x := range v {
|
||||
order.PutUint16(bs[2*i:], uint16(x))
|
||||
}
|
||||
case *uint16:
|
||||
bs = b[:2]
|
||||
order.PutUint16(bs, *v)
|
||||
case uint16:
|
||||
bs = b[:2]
|
||||
order.PutUint16(bs, v)
|
||||
case []uint16:
|
||||
for i, x := range v {
|
||||
order.PutUint16(bs[2*i:], x)
|
||||
}
|
||||
case *int32:
|
||||
bs = b[:4]
|
||||
order.PutUint32(bs, uint32(*v))
|
||||
case int32:
|
||||
bs = b[:4]
|
||||
order.PutUint32(bs, uint32(v))
|
||||
case []int32:
|
||||
for i, x := range v {
|
||||
order.PutUint32(bs[4*i:], uint32(x))
|
||||
}
|
||||
case *uint32:
|
||||
bs = b[:4]
|
||||
order.PutUint32(bs, *v)
|
||||
case uint32:
|
||||
bs = b[:4]
|
||||
order.PutUint32(bs, v)
|
||||
case []uint32:
|
||||
for i, x := range v {
|
||||
order.PutUint32(bs[4*i:], x)
|
||||
}
|
||||
case *int64:
|
||||
bs = b[:8]
|
||||
order.PutUint64(bs, uint64(*v))
|
||||
case int64:
|
||||
bs = b[:8]
|
||||
order.PutUint64(bs, uint64(v))
|
||||
case []int64:
|
||||
for i, x := range v {
|
||||
order.PutUint64(bs[8*i:], uint64(x))
|
||||
}
|
||||
case *uint64:
|
||||
bs = b[:8]
|
||||
order.PutUint64(bs, *v)
|
||||
case uint64:
|
||||
bs = b[:8]
|
||||
order.PutUint64(bs, v)
|
||||
case []uint64:
|
||||
for i, x := range v {
|
||||
order.PutUint64(bs[8*i:], x)
|
||||
}
|
||||
}
|
||||
_, err := w.Write(bs)
|
||||
return err
|
||||
}
|
||||
|
||||
// Fallback to reflect-based encoding.
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
size := dataSize(v)
|
||||
if size < 0 {
|
||||
return errors.New("binary.Write: invalid type " + reflect.TypeOf(data).String())
|
||||
}
|
||||
buf := make([]byte, size)
|
||||
e := &encoder{order: order, buf: buf}
|
||||
e.value(v)
|
||||
_, err := w.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
// Size returns how many bytes Write would generate to encode the value v, which
|
||||
// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
|
||||
// If v is neither of these, Size returns -1.
|
||||
func Size(v interface{}) int {
|
||||
return dataSize(reflect.Indirect(reflect.ValueOf(v)))
|
||||
}
|
||||
|
||||
// dataSize returns the number of bytes the actual data represented by v occupies in memory.
|
||||
// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice
|
||||
// it returns the length of the slice times the element size and does not count the memory
|
||||
// occupied by the header. If the type of v is not acceptable, dataSize returns -1.
|
||||
func dataSize(v reflect.Value) int {
|
||||
if v.Kind() == reflect.Slice {
|
||||
if s := sizeof(v.Type().Elem()); s >= 0 {
|
||||
return s * v.Len()
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return sizeof(v.Type())
|
||||
}
|
||||
|
||||
// sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable.
|
||||
func sizeof(t reflect.Type) int {
|
||||
switch t.Kind() {
|
||||
case reflect.Array:
|
||||
if s := sizeof(t.Elem()); s >= 0 {
|
||||
return s * t.Len()
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
sum := 0
|
||||
for i, n := 0, t.NumField(); i < n; i++ {
|
||||
s := sizeof(t.Field(i).Type)
|
||||
if s < 0 {
|
||||
return -1
|
||||
}
|
||||
sum += s
|
||||
}
|
||||
return sum
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.Ptr:
|
||||
return int(t.Size())
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
type coder struct {
|
||||
order ByteOrder
|
||||
buf []byte
|
||||
}
|
||||
|
||||
type decoder coder
|
||||
type encoder coder
|
||||
|
||||
func (d *decoder) uint8() uint8 {
|
||||
x := d.buf[0]
|
||||
d.buf = d.buf[1:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (e *encoder) uint8(x uint8) {
|
||||
e.buf[0] = x
|
||||
e.buf = e.buf[1:]
|
||||
}
|
||||
|
||||
func (d *decoder) uint16() uint16 {
|
||||
x := d.order.Uint16(d.buf[0:2])
|
||||
d.buf = d.buf[2:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (e *encoder) uint16(x uint16) {
|
||||
e.order.PutUint16(e.buf[0:2], x)
|
||||
e.buf = e.buf[2:]
|
||||
}
|
||||
|
||||
func (d *decoder) uint32() uint32 {
|
||||
x := d.order.Uint32(d.buf[0:4])
|
||||
d.buf = d.buf[4:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (e *encoder) uint32(x uint32) {
|
||||
e.order.PutUint32(e.buf[0:4], x)
|
||||
e.buf = e.buf[4:]
|
||||
}
|
||||
|
||||
func (d *decoder) uint64() uint64 {
|
||||
x := d.order.Uint64(d.buf[0:8])
|
||||
d.buf = d.buf[8:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (e *encoder) uint64(x uint64) {
|
||||
e.order.PutUint64(e.buf[0:8], x)
|
||||
e.buf = e.buf[8:]
|
||||
}
|
||||
|
||||
func (d *decoder) int8() int8 { return int8(d.uint8()) }
|
||||
|
||||
func (e *encoder) int8(x int8) { e.uint8(uint8(x)) }
|
||||
|
||||
func (d *decoder) int16() int16 { return int16(d.uint16()) }
|
||||
|
||||
func (e *encoder) int16(x int16) { e.uint16(uint16(x)) }
|
||||
|
||||
func (d *decoder) int32() int32 { return int32(d.uint32()) }
|
||||
|
||||
func (e *encoder) int32(x int32) { e.uint32(uint32(x)) }
|
||||
|
||||
func (d *decoder) int64() int64 { return int64(d.uint64()) }
|
||||
|
||||
func (e *encoder) int64(x int64) { e.uint64(uint64(x)) }
|
||||
|
||||
func (d *decoder) value(v reflect.Value) {
|
||||
switch v.Kind() {
|
||||
case reflect.Array:
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
d.value(v.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
l := v.NumField()
|
||||
for i := 0; i < l; i++ {
|
||||
// Note: Calling v.CanSet() below is an optimization.
|
||||
// It would be sufficient to check the field name,
|
||||
// but creating the StructField info for each field is
|
||||
// costly (run "go test -bench=ReadStruct" and compare
|
||||
// results when making changes to this code).
|
||||
if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
|
||||
d.value(v)
|
||||
} else {
|
||||
d.skip(v)
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
d.value(v.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Int8:
|
||||
v.SetInt(int64(d.int8()))
|
||||
case reflect.Int16:
|
||||
v.SetInt(int64(d.int16()))
|
||||
case reflect.Int32:
|
||||
v.SetInt(int64(d.int32()))
|
||||
case reflect.Int64:
|
||||
v.SetInt(d.int64())
|
||||
|
||||
case reflect.Uint8:
|
||||
v.SetUint(uint64(d.uint8()))
|
||||
case reflect.Uint16:
|
||||
v.SetUint(uint64(d.uint16()))
|
||||
case reflect.Uint32:
|
||||
v.SetUint(uint64(d.uint32()))
|
||||
case reflect.Uint64:
|
||||
v.SetUint(d.uint64())
|
||||
|
||||
case reflect.Float32:
|
||||
v.SetFloat(float64(math.Float32frombits(d.uint32())))
|
||||
case reflect.Float64:
|
||||
v.SetFloat(math.Float64frombits(d.uint64()))
|
||||
|
||||
case reflect.Complex64:
|
||||
v.SetComplex(complex(
|
||||
float64(math.Float32frombits(d.uint32())),
|
||||
float64(math.Float32frombits(d.uint32())),
|
||||
))
|
||||
case reflect.Complex128:
|
||||
v.SetComplex(complex(
|
||||
math.Float64frombits(d.uint64()),
|
||||
math.Float64frombits(d.uint64()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) value(v reflect.Value) {
|
||||
switch v.Kind() {
|
||||
case reflect.Array:
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
e.value(v.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
l := v.NumField()
|
||||
for i := 0; i < l; i++ {
|
||||
// see comment for corresponding code in decoder.value()
|
||||
if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
|
||||
e.value(v)
|
||||
} else {
|
||||
e.skip(v)
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
e.value(v.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int8:
|
||||
e.int8(int8(v.Int()))
|
||||
case reflect.Int16:
|
||||
e.int16(int16(v.Int()))
|
||||
case reflect.Int32:
|
||||
e.int32(int32(v.Int()))
|
||||
case reflect.Int64:
|
||||
e.int64(v.Int())
|
||||
}
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Uint8:
|
||||
e.uint8(uint8(v.Uint()))
|
||||
case reflect.Uint16:
|
||||
e.uint16(uint16(v.Uint()))
|
||||
case reflect.Uint32:
|
||||
e.uint32(uint32(v.Uint()))
|
||||
case reflect.Uint64:
|
||||
e.uint64(v.Uint())
|
||||
}
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Float32:
|
||||
e.uint32(math.Float32bits(float32(v.Float())))
|
||||
case reflect.Float64:
|
||||
e.uint64(math.Float64bits(v.Float()))
|
||||
}
|
||||
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Complex64:
|
||||
x := v.Complex()
|
||||
e.uint32(math.Float32bits(float32(real(x))))
|
||||
e.uint32(math.Float32bits(float32(imag(x))))
|
||||
case reflect.Complex128:
|
||||
x := v.Complex()
|
||||
e.uint64(math.Float64bits(real(x)))
|
||||
e.uint64(math.Float64bits(imag(x)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *decoder) skip(v reflect.Value) {
|
||||
d.buf = d.buf[dataSize(v):]
|
||||
}
|
||||
|
||||
func (e *encoder) skip(v reflect.Value) {
|
||||
n := dataSize(v)
|
||||
for i := range e.buf[0:n] {
|
||||
e.buf[i] = 0
|
||||
}
|
||||
e.buf = e.buf[n:]
|
||||
}
|
||||
|
||||
// intDataSize returns the size of the data required to represent the data when encoded.
|
||||
// It returns zero if the type cannot be implemented by the fast path in Read or Write.
|
||||
func intDataSize(data interface{}) int {
|
||||
switch data := data.(type) {
|
||||
case int8, *int8, *uint8:
|
||||
return 1
|
||||
case []int8:
|
||||
return len(data)
|
||||
case []uint8:
|
||||
return len(data)
|
||||
case int16, *int16, *uint16:
|
||||
return 2
|
||||
case []int16:
|
||||
return 2 * len(data)
|
||||
case []uint16:
|
||||
return 2 * len(data)
|
||||
case int32, *int32, *uint32:
|
||||
return 4
|
||||
case []int32:
|
||||
return 4 * len(data)
|
||||
case []uint32:
|
||||
return 4 * len(data)
|
||||
case int64, *int64, *uint64:
|
||||
return 8
|
||||
case []int64:
|
||||
return 8 * len(data)
|
||||
case []uint64:
|
||||
return 8 * len(data)
|
||||
}
|
||||
return 0
|
||||
}
|
|
@ -1,369 +0,0 @@
|
|||
package common
|
||||
|
||||
//
|
||||
// gopsutil is a port of psutil(http://pythonhosted.org/psutil/).
|
||||
// This covers these architectures.
|
||||
// - linux (amd64, arm)
|
||||
// - freebsd (amd64)
|
||||
// - windows (amd64)
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
Timeout = 3 * time.Second
|
||||
ErrTimeout = errors.New("command timed out")
|
||||
)
|
||||
|
||||
type Invoker interface {
|
||||
Command(string, ...string) ([]byte, error)
|
||||
CommandWithContext(context.Context, string, ...string) ([]byte, error)
|
||||
}
|
||||
|
||||
type Invoke struct{}
|
||||
|
||||
func (i Invoke) Command(name string, arg ...string) ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), Timeout)
|
||||
defer cancel()
|
||||
return i.CommandWithContext(ctx, name, arg...)
|
||||
}
|
||||
|
||||
func (i Invoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
|
||||
cmd := exec.CommandContext(ctx, name, arg...)
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = &buf
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type FakeInvoke struct {
|
||||
Suffix string // Suffix species expected file name suffix such as "fail"
|
||||
Error error // If Error specfied, return the error.
|
||||
}
|
||||
|
||||
// Command in FakeInvoke returns from expected file if exists.
|
||||
func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) {
|
||||
if i.Error != nil {
|
||||
return []byte{}, i.Error
|
||||
}
|
||||
|
||||
arch := runtime.GOOS
|
||||
|
||||
commandName := filepath.Base(name)
|
||||
|
||||
fname := strings.Join(append([]string{commandName}, arg...), "")
|
||||
fname = url.QueryEscape(fname)
|
||||
fpath := path.Join("testdata", arch, fname)
|
||||
if i.Suffix != "" {
|
||||
fpath += "_" + i.Suffix
|
||||
}
|
||||
if PathExists(fpath) {
|
||||
return ioutil.ReadFile(fpath)
|
||||
}
|
||||
return []byte{}, fmt.Errorf("could not find testdata: %s", fpath)
|
||||
}
|
||||
|
||||
func (i FakeInvoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
|
||||
return i.Command(name, arg...)
|
||||
}
|
||||
|
||||
var ErrNotImplementedError = errors.New("not implemented yet")
|
||||
|
||||
// ReadFile reads contents from a file
|
||||
func ReadFile(filename string) (string, error) {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
// ReadLines reads contents from a file and splits them by new lines.
|
||||
// A convenience wrapper to ReadLinesOffsetN(filename, 0, -1).
|
||||
func ReadLines(filename string) ([]string, error) {
|
||||
return ReadLinesOffsetN(filename, 0, -1)
|
||||
}
|
||||
|
||||
// ReadLines reads contents from file and splits them by new line.
|
||||
// The offset tells at which line number to start.
|
||||
// The count determines the number of lines to read (starting from offset):
|
||||
// n >= 0: at most n lines
|
||||
// n < 0: whole file
|
||||
func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return []string{""}, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var ret []string
|
||||
|
||||
r := bufio.NewReader(f)
|
||||
for i := 0; i < n+int(offset) || n < 0; i++ {
|
||||
line, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if i < int(offset) {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, strings.Trim(line, "\n"))
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func IntToString(orig []int8) string {
|
||||
ret := make([]byte, len(orig))
|
||||
size := -1
|
||||
for i, o := range orig {
|
||||
if o == 0 {
|
||||
size = i
|
||||
break
|
||||
}
|
||||
ret[i] = byte(o)
|
||||
}
|
||||
if size == -1 {
|
||||
size = len(orig)
|
||||
}
|
||||
|
||||
return string(ret[0:size])
|
||||
}
|
||||
|
||||
func UintToString(orig []uint8) string {
|
||||
ret := make([]byte, len(orig))
|
||||
size := -1
|
||||
for i, o := range orig {
|
||||
if o == 0 {
|
||||
size = i
|
||||
break
|
||||
}
|
||||
ret[i] = byte(o)
|
||||
}
|
||||
if size == -1 {
|
||||
size = len(orig)
|
||||
}
|
||||
|
||||
return string(ret[0:size])
|
||||
}
|
||||
|
||||
func ByteToString(orig []byte) string {
|
||||
n := -1
|
||||
l := -1
|
||||
for i, b := range orig {
|
||||
// skip left side null
|
||||
if l == -1 && b == 0 {
|
||||
continue
|
||||
}
|
||||
if l == -1 {
|
||||
l = i
|
||||
}
|
||||
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
n = i + 1
|
||||
}
|
||||
if n == -1 {
|
||||
return string(orig)
|
||||
}
|
||||
return string(orig[l:n])
|
||||
}
|
||||
|
||||
// ReadInts reads contents from single line file and returns them as []int32.
|
||||
func ReadInts(filename string) ([]int64, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return []int64{}, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var ret []int64
|
||||
|
||||
r := bufio.NewReader(f)
|
||||
|
||||
// The int files that this is concerned with should only be one liners.
|
||||
line, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
return []int64{}, err
|
||||
}
|
||||
|
||||
i, err := strconv.ParseInt(strings.Trim(line, "\n"), 10, 32)
|
||||
if err != nil {
|
||||
return []int64{}, err
|
||||
}
|
||||
ret = append(ret, i)
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Parse Hex to uint32 without error
|
||||
func HexToUint32(hex string) uint32 {
|
||||
vv, _ := strconv.ParseUint(hex, 16, 32)
|
||||
return uint32(vv)
|
||||
}
|
||||
|
||||
// Parse to int32 without error
|
||||
func mustParseInt32(val string) int32 {
|
||||
vv, _ := strconv.ParseInt(val, 10, 32)
|
||||
return int32(vv)
|
||||
}
|
||||
|
||||
// Parse to uint64 without error
|
||||
func mustParseUint64(val string) uint64 {
|
||||
vv, _ := strconv.ParseInt(val, 10, 64)
|
||||
return uint64(vv)
|
||||
}
|
||||
|
||||
// Parse to Float64 without error
|
||||
func mustParseFloat64(val string) float64 {
|
||||
vv, _ := strconv.ParseFloat(val, 64)
|
||||
return vv
|
||||
}
|
||||
|
||||
// StringsHas checks the target string slice contains src or not
|
||||
func StringsHas(target []string, src string) bool {
|
||||
for _, t := range target {
|
||||
if strings.TrimSpace(t) == src {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// StringsContains checks the src in any string of the target string slice
|
||||
func StringsContains(target []string, src string) bool {
|
||||
for _, t := range target {
|
||||
if strings.Contains(t, src) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IntContains checks the src in any int of the target int slice.
|
||||
func IntContains(target []int, src int) bool {
|
||||
for _, t := range target {
|
||||
if src == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// get struct attributes.
|
||||
// This method is used only for debugging platform dependent code.
|
||||
func attributes(m interface{}) map[string]reflect.Type {
|
||||
typ := reflect.TypeOf(m)
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
|
||||
attrs := make(map[string]reflect.Type)
|
||||
if typ.Kind() != reflect.Struct {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
p := typ.Field(i)
|
||||
if !p.Anonymous {
|
||||
attrs[p.Name] = p.Type
|
||||
}
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
func PathExists(filename string) bool {
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//GetEnv retrieves the environment variable key. If it does not exist it returns the default.
|
||||
func GetEnv(key string, dfault string, combineWith ...string) string {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
value = dfault
|
||||
}
|
||||
|
||||
switch len(combineWith) {
|
||||
case 0:
|
||||
return value
|
||||
case 1:
|
||||
return filepath.Join(value, combineWith[0])
|
||||
default:
|
||||
all := make([]string, len(combineWith)+1)
|
||||
all[0] = value
|
||||
copy(all[1:], combineWith)
|
||||
return filepath.Join(all...)
|
||||
}
|
||||
}
|
||||
|
||||
func HostProc(combineWith ...string) string {
|
||||
return GetEnv("HOST_PROC", "/proc", combineWith...)
|
||||
}
|
||||
|
||||
func HostSys(combineWith ...string) string {
|
||||
return GetEnv("HOST_SYS", "/sys", combineWith...)
|
||||
}
|
||||
|
||||
func HostEtc(combineWith ...string) string {
|
||||
return GetEnv("HOST_ETC", "/etc", combineWith...)
|
||||
}
|
||||
|
||||
func HostVar(combineWith ...string) string {
|
||||
return GetEnv("HOST_VAR", "/var", combineWith...)
|
||||
}
|
||||
|
||||
func HostRun(combineWith ...string) string {
|
||||
return GetEnv("HOST_RUN", "/run", combineWith...)
|
||||
}
|
||||
|
||||
func HostDev(combineWith ...string) string {
|
||||
return GetEnv("HOST_DEV", "/dev", combineWith...)
|
||||
}
|
||||
|
||||
// getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running
|
||||
// sysctl commands (see DoSysctrl).
|
||||
func getSysctrlEnv(env []string) []string {
|
||||
foundLC := false
|
||||
for i, line := range env {
|
||||
if strings.HasPrefix(line, "LC_ALL") {
|
||||
env[i] = "LC_ALL=C"
|
||||
foundLC = true
|
||||
}
|
||||
}
|
||||
if !foundLC {
|
||||
env = append(env, "LC_ALL=C")
|
||||
}
|
||||
return env
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func DoSysctrlWithContext(ctx context.Context, mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, sysctl, "-n", mib)
|
||||
cmd.Env = getSysctrlEnv(os.Environ())
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
v := strings.Replace(string(out), "{ ", "", 1)
|
||||
v = strings.Replace(string(v), " }", "", 1)
|
||||
values := strings.Fields(string(v))
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func CallSyscall(mib []int32) ([]byte, uint64, error) {
|
||||
miblen := uint64(len(mib))
|
||||
|
||||
// get required buffer size
|
||||
length := uint64(0)
|
||||
_, _, err := unix.Syscall6(
|
||||
202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
uintptr(miblen),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
if length == 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
// get proc info itself
|
||||
buf := make([]byte, length)
|
||||
_, _, err = unix.Syscall6(
|
||||
202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
uintptr(miblen),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
return buf, length, err
|
||||
}
|
||||
|
||||
return buf, length, nil
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
//go:build freebsd || openbsd
|
||||
// +build freebsd openbsd
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func SysctlUint(mib string) (uint64, error) {
|
||||
buf, err := unix.SysctlRaw(mib)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(buf) == 8 { // 64 bit
|
||||
return *(*uint64)(unsafe.Pointer(&buf[0])), nil
|
||||
}
|
||||
if len(buf) == 4 { // 32bit
|
||||
t := *(*uint32)(unsafe.Pointer(&buf[0]))
|
||||
return uint64(t), nil
|
||||
}
|
||||
return 0, fmt.Errorf("unexpected size: %s, %d", mib, len(buf))
|
||||
}
|
||||
|
||||
func DoSysctrl(mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
cmd := exec.Command(sysctl, "-n", mib)
|
||||
cmd.Env = getSysctrlEnv(os.Environ())
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
v := strings.Replace(string(out), "{ ", "", 1)
|
||||
v = strings.Replace(string(v), " }", "", 1)
|
||||
values := strings.Fields(string(v))
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func CallSyscall(mib []int32) ([]byte, uint64, error) {
|
||||
mibptr := unsafe.Pointer(&mib[0])
|
||||
miblen := uint64(len(mib))
|
||||
|
||||
// get required buffer size
|
||||
length := uint64(0)
|
||||
_, _, err := unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
uintptr(mibptr),
|
||||
uintptr(miblen),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
if length == 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
// get proc info itself
|
||||
buf := make([]byte, length)
|
||||
_, _, err = unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
uintptr(mibptr),
|
||||
uintptr(miblen),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
return buf, length, err
|
||||
}
|
||||
|
||||
return buf, length, nil
|
||||
}
|
|
@ -1,266 +0,0 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func DoSysctrl(mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
cmd := exec.Command(sysctl, "-n", mib)
|
||||
cmd.Env = getSysctrlEnv(os.Environ())
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
v := strings.Replace(string(out), "{ ", "", 1)
|
||||
v = strings.Replace(string(v), " }", "", 1)
|
||||
values := strings.Fields(string(v))
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func NumProcs() (uint64, error) {
|
||||
f, err := os.Open(HostProc())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
list, err := f.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var cnt uint64
|
||||
|
||||
for _, v := range list {
|
||||
if _, err = strconv.ParseUint(v, 10, 64); err == nil {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
|
||||
return cnt, nil
|
||||
}
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
|
||||
system, role, err := Virtualization()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
statFile := "stat"
|
||||
if system == "lxc" && role == "guest" {
|
||||
// if lxc, /proc/uptime is used.
|
||||
statFile = "uptime"
|
||||
} else if system == "docker" && role == "guest" {
|
||||
// also docker, guest
|
||||
statFile = "uptime"
|
||||
}
|
||||
|
||||
filename := HostProc(statFile)
|
||||
lines, err := ReadLines(filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if statFile == "stat" {
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "btime") {
|
||||
f := strings.Fields(line)
|
||||
if len(f) != 2 {
|
||||
return 0, fmt.Errorf("wrong btime format")
|
||||
}
|
||||
b, err := strconv.ParseInt(f[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t := uint64(b)
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
} else if statFile == "uptime" {
|
||||
if len(lines) != 1 {
|
||||
return 0, fmt.Errorf("wrong uptime format")
|
||||
}
|
||||
f := strings.Fields(lines[0])
|
||||
b, err := strconv.ParseFloat(f[0], 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t := uint64(time.Now().Unix()) - uint64(b)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("could not find btime")
|
||||
}
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
|
||||
var system string
|
||||
var role string
|
||||
|
||||
filename := HostProc("xen")
|
||||
if PathExists(filename) {
|
||||
system = "xen"
|
||||
role = "guest" // assume guest
|
||||
|
||||
if PathExists(filepath.Join(filename, "capabilities")) {
|
||||
contents, err := ReadLines(filepath.Join(filename, "capabilities"))
|
||||
if err == nil {
|
||||
if StringsContains(contents, "control_d") {
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc("modules")
|
||||
if PathExists(filename) {
|
||||
contents, err := ReadLines(filename)
|
||||
if err == nil {
|
||||
if StringsContains(contents, "kvm") {
|
||||
system = "kvm"
|
||||
role = "host"
|
||||
} else if StringsContains(contents, "vboxdrv") {
|
||||
system = "vbox"
|
||||
role = "host"
|
||||
} else if StringsContains(contents, "vboxguest") {
|
||||
system = "vbox"
|
||||
role = "guest"
|
||||
} else if StringsContains(contents, "vmware") {
|
||||
system = "vmware"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc("cpuinfo")
|
||||
if PathExists(filename) {
|
||||
contents, err := ReadLines(filename)
|
||||
if err == nil {
|
||||
if StringsContains(contents, "QEMU Virtual CPU") ||
|
||||
StringsContains(contents, "Common KVM processor") ||
|
||||
StringsContains(contents, "Common 32-bit KVM processor") {
|
||||
system = "kvm"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc("bus/pci/devices")
|
||||
if PathExists(filename) {
|
||||
contents, err := ReadLines(filename)
|
||||
if err == nil {
|
||||
if StringsContains(contents, "virtio-pci") {
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc()
|
||||
if PathExists(filepath.Join(filename, "bc", "0")) {
|
||||
system = "openvz"
|
||||
role = "host"
|
||||
} else if PathExists(filepath.Join(filename, "vz")) {
|
||||
system = "openvz"
|
||||
role = "guest"
|
||||
}
|
||||
|
||||
// not use dmidecode because it requires root
|
||||
if PathExists(filepath.Join(filename, "self", "status")) {
|
||||
contents, err := ReadLines(filepath.Join(filename, "self", "status"))
|
||||
if err == nil {
|
||||
|
||||
if StringsContains(contents, "s_context:") ||
|
||||
StringsContains(contents, "VxID:") {
|
||||
system = "linux-vserver"
|
||||
}
|
||||
// TODO: guest or host
|
||||
}
|
||||
}
|
||||
|
||||
if PathExists(filepath.Join(filename, "1", "environ")) {
|
||||
contents, err := ReadFile(filepath.Join(filename, "1", "environ"))
|
||||
|
||||
if err == nil {
|
||||
if strings.Contains(contents, "container=lxc") {
|
||||
system = "lxc"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if PathExists(filepath.Join(filename, "self", "cgroup")) {
|
||||
contents, err := ReadLines(filepath.Join(filename, "self", "cgroup"))
|
||||
if err == nil {
|
||||
if StringsContains(contents, "lxc") {
|
||||
system = "lxc"
|
||||
role = "guest"
|
||||
} else if StringsContains(contents, "docker") {
|
||||
system = "docker"
|
||||
role = "guest"
|
||||
} else if StringsContains(contents, "machine-rkt") {
|
||||
system = "rkt"
|
||||
role = "guest"
|
||||
} else if PathExists("/usr/bin/lxc-version") {
|
||||
system = "lxc"
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if PathExists(HostEtc("os-release")) {
|
||||
p, _, err := GetOSRelease()
|
||||
if err == nil && p == "coreos" {
|
||||
system = "rkt" // Is it true?
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
return system, role, nil
|
||||
}
|
||||
|
||||
func GetOSRelease() (platform string, version string, err error) {
|
||||
contents, err := ReadLines(HostEtc("os-release"))
|
||||
if err != nil {
|
||||
return "", "", nil // return empty
|
||||
}
|
||||
for _, line := range contents {
|
||||
field := strings.Split(line, "=")
|
||||
if len(field) < 2 {
|
||||
continue
|
||||
}
|
||||
switch field[0] {
|
||||
case "ID": // use ID for lowercase
|
||||
platform = trimQuotes(field[1])
|
||||
case "VERSION":
|
||||
version = trimQuotes(field[1])
|
||||
}
|
||||
}
|
||||
return platform, version, nil
|
||||
}
|
||||
|
||||
// Remove quotes of the source string
|
||||
func trimQuotes(s string) string {
|
||||
if len(s) >= 2 {
|
||||
if s[0] == '"' && s[len(s)-1] == '"' {
|
||||
return s[1 : len(s)-1]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
//go:build openbsd
|
||||
// +build openbsd
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func DoSysctrl(mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
cmd := exec.Command(sysctl, "-n", mib)
|
||||
cmd.Env = getSysctrlEnv(os.Environ())
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
v := strings.Replace(string(out), "{ ", "", 1)
|
||||
v = strings.Replace(string(v), " }", "", 1)
|
||||
values := strings.Fields(string(v))
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func CallSyscall(mib []int32) ([]byte, uint64, error) {
|
||||
mibptr := unsafe.Pointer(&mib[0])
|
||||
miblen := uint64(len(mib))
|
||||
|
||||
// get required buffer size
|
||||
length := uint64(0)
|
||||
_, _, err := unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
uintptr(mibptr),
|
||||
uintptr(miblen),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
if length == 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
// get proc info itself
|
||||
buf := make([]byte, length)
|
||||
_, _, err = unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
uintptr(mibptr),
|
||||
uintptr(miblen),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
return buf, length, err
|
||||
}
|
||||
|
||||
return buf, length, nil
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
//go:build linux || freebsd || darwin || openbsd
|
||||
// +build linux freebsd darwin openbsd
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ...string) ([]string, error) {
|
||||
var cmd []string
|
||||
if pid == 0 { // will get from all processes.
|
||||
cmd = []string{"-a", "-n", "-P"}
|
||||
} else {
|
||||
cmd = []string{"-a", "-n", "-P", "-p", strconv.Itoa(int(pid))}
|
||||
}
|
||||
cmd = append(cmd, args...)
|
||||
lsof, err := exec.LookPath("lsof")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, lsof, cmd...)
|
||||
if err != nil {
|
||||
// if no pid found, lsof returns code 1.
|
||||
if err.Error() == "exit status 1" && len(out) == 0 {
|
||||
return []string{}, nil
|
||||
}
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
|
||||
var ret []string
|
||||
for _, l := range lines[1:] {
|
||||
if len(l) == 0 {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, l)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) {
|
||||
var cmd []string
|
||||
cmd = []string{"-P", strconv.Itoa(int(pid))}
|
||||
pgrep, err := exec.LookPath("pgrep")
|
||||
if err != nil {
|
||||
return []int32{}, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, pgrep, cmd...)
|
||||
if err != nil {
|
||||
return []int32{}, err
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
ret := make([]int32, 0, len(lines))
|
||||
for _, l := range lines {
|
||||
if len(l) == 0 {
|
||||
continue
|
||||
}
|
||||
i, err := strconv.Atoi(l)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, int32(i))
|
||||
}
|
||||
return ret, nil
|
||||
}
|
|
@ -1,231 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gofiber/fiber/v3/internal/wmi"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// for double values
|
||||
type PDH_FMT_COUNTERVALUE_DOUBLE struct {
|
||||
CStatus uint32
|
||||
DoubleValue float64
|
||||
}
|
||||
|
||||
// for 64 bit integer values
|
||||
type PDH_FMT_COUNTERVALUE_LARGE struct {
|
||||
CStatus uint32
|
||||
LargeValue int64
|
||||
}
|
||||
|
||||
// for long values
|
||||
type PDH_FMT_COUNTERVALUE_LONG struct {
|
||||
CStatus uint32
|
||||
LongValue int32
|
||||
padding [4]byte
|
||||
}
|
||||
|
||||
// windows system const
|
||||
const (
|
||||
ERROR_SUCCESS = 0
|
||||
ERROR_FILE_NOT_FOUND = 2
|
||||
DRIVE_REMOVABLE = 2
|
||||
DRIVE_FIXED = 3
|
||||
HKEY_LOCAL_MACHINE = 0x80000002
|
||||
RRF_RT_REG_SZ = 0x00000002
|
||||
RRF_RT_REG_DWORD = 0x00000010
|
||||
PDH_FMT_LONG = 0x00000100
|
||||
PDH_FMT_DOUBLE = 0x00000200
|
||||
PDH_FMT_LARGE = 0x00000400
|
||||
PDH_INVALID_DATA = 0xc0000bc6
|
||||
PDH_INVALID_HANDLE = 0xC0000bbc
|
||||
PDH_NO_DATA = 0x800007d5
|
||||
)
|
||||
|
||||
const (
|
||||
ProcessBasicInformation = 0
|
||||
ProcessWow64Information = 26
|
||||
)
|
||||
|
||||
var (
|
||||
Modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
ModNt = windows.NewLazySystemDLL("ntdll.dll")
|
||||
ModPdh = windows.NewLazySystemDLL("pdh.dll")
|
||||
ModPsapi = windows.NewLazySystemDLL("psapi.dll")
|
||||
|
||||
ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes")
|
||||
ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation")
|
||||
ProcRtlGetNativeSystemInformation = ModNt.NewProc("RtlGetNativeSystemInformation")
|
||||
ProcRtlNtStatusToDosError = ModNt.NewProc("RtlNtStatusToDosError")
|
||||
ProcNtQueryInformationProcess = ModNt.NewProc("NtQueryInformationProcess")
|
||||
ProcNtReadVirtualMemory = ModNt.NewProc("NtReadVirtualMemory")
|
||||
ProcNtWow64QueryInformationProcess64 = ModNt.NewProc("NtWow64QueryInformationProcess64")
|
||||
ProcNtWow64ReadVirtualMemory64 = ModNt.NewProc("NtWow64ReadVirtualMemory64")
|
||||
|
||||
PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery")
|
||||
PdhAddEnglishCounterW = ModPdh.NewProc("PdhAddEnglishCounterW")
|
||||
PdhAddCounter = ModPdh.NewProc("PdhAddCounterW")
|
||||
PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
|
||||
PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
|
||||
PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery")
|
||||
|
||||
procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW")
|
||||
)
|
||||
|
||||
type FILETIME struct {
|
||||
DwLowDateTime uint32
|
||||
DwHighDateTime uint32
|
||||
}
|
||||
|
||||
// borrowed from net/interface_windows.go
|
||||
func BytePtrToString(p *uint8) string {
|
||||
a := (*[10000]uint8)(unsafe.Pointer(p))
|
||||
i := 0
|
||||
for a[i] != 0 {
|
||||
i++
|
||||
}
|
||||
return string(a[:i])
|
||||
}
|
||||
|
||||
// CounterInfo
|
||||
// copied from https://github.com/mackerelio/mackerel-agent/
|
||||
type CounterInfo struct {
|
||||
PostName string
|
||||
CounterName string
|
||||
Counter windows.Handle
|
||||
}
|
||||
|
||||
// CreateQuery XXX
|
||||
// copied from https://github.com/mackerelio/mackerel-agent/
|
||||
func CreateQuery() (windows.Handle, error) {
|
||||
var query windows.Handle
|
||||
r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query)))
|
||||
if r != 0 {
|
||||
return 0, err
|
||||
}
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// CreateCounter XXX
|
||||
func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, error) {
|
||||
var counter windows.Handle
|
||||
r, _, err := PdhAddCounter.Call(
|
||||
uintptr(query),
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cname))),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&counter)))
|
||||
if r != 0 {
|
||||
return nil, err
|
||||
}
|
||||
return &CounterInfo{
|
||||
PostName: pname,
|
||||
CounterName: cname,
|
||||
Counter: counter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetCounterValue get counter value from handle
|
||||
// adapted from https://github.com/mackerelio/mackerel-agent/
|
||||
func GetCounterValue(counter windows.Handle) (float64, error) {
|
||||
var value PDH_FMT_COUNTERVALUE_DOUBLE
|
||||
r, _, err := PdhGetFormattedCounterValue.Call(uintptr(counter), PDH_FMT_DOUBLE, uintptr(0), uintptr(unsafe.Pointer(&value)))
|
||||
if r != 0 && r != PDH_INVALID_DATA {
|
||||
return 0.0, err
|
||||
}
|
||||
return value.DoubleValue, nil
|
||||
}
|
||||
|
||||
type Win32PerformanceCounter struct {
|
||||
PostName string
|
||||
CounterName string
|
||||
Query windows.Handle
|
||||
Counter windows.Handle
|
||||
}
|
||||
|
||||
func NewWin32PerformanceCounter(postName, counterName string) (*Win32PerformanceCounter, error) {
|
||||
query, err := CreateQuery()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var counter = Win32PerformanceCounter{
|
||||
Query: query,
|
||||
PostName: postName,
|
||||
CounterName: counterName,
|
||||
}
|
||||
r, _, err := PdhAddEnglishCounterW.Call(
|
||||
uintptr(counter.Query),
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(counter.CounterName))),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&counter.Counter)),
|
||||
)
|
||||
if r != 0 {
|
||||
return nil, err
|
||||
}
|
||||
return &counter, nil
|
||||
}
|
||||
|
||||
func (w *Win32PerformanceCounter) GetValue() (float64, error) {
|
||||
r, _, err := PdhCollectQueryData.Call(uintptr(w.Query))
|
||||
if r != 0 && err != nil {
|
||||
if r == PDH_NO_DATA {
|
||||
return 0.0, fmt.Errorf("%w: this counter has not data", err)
|
||||
}
|
||||
return 0.0, err
|
||||
}
|
||||
|
||||
return GetCounterValue(w.Counter)
|
||||
}
|
||||
|
||||
func ProcessorQueueLengthCounter() (*Win32PerformanceCounter, error) {
|
||||
return NewWin32PerformanceCounter("processor_queue_length", `\System\Processor Queue Length`)
|
||||
}
|
||||
|
||||
// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging
|
||||
func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error {
|
||||
if _, ok := ctx.Deadline(); !ok {
|
||||
ctxTimeout, cancel := context.WithTimeout(ctx, Timeout)
|
||||
defer cancel()
|
||||
ctx = ctxTimeout
|
||||
}
|
||||
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
errChan <- wmi.Query(query, dst, connectServerArgs...)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case err := <-errChan:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Convert paths using native DOS format like:
|
||||
// "\Device\HarddiskVolume1\Windows\systemew\file.txt"
|
||||
// into:
|
||||
// "C:\Windows\systemew\file.txt"
|
||||
func ConvertDOSPath(p string) string {
|
||||
rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`)
|
||||
|
||||
for d := 'A'; d <= 'Z'; d++ {
|
||||
szDeviceName := string(d) + ":"
|
||||
szTarget := make([]uint16, 512)
|
||||
ret, _, _ := procQueryDosDeviceW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(szDeviceName))),
|
||||
uintptr(unsafe.Pointer(&szTarget[0])),
|
||||
uintptr(len(szTarget)))
|
||||
if ret != 0 && windows.UTF16ToString(szTarget[:]) == rawDrive {
|
||||
return filepath.Join(szDeviceName, p[len(rawDrive):])
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Sleep awaits for provided interval.
|
||||
// Can be interrupted by context cancelation.
|
||||
func Sleep(ctx context.Context, interval time.Duration) error {
|
||||
var timer = time.NewTimer(interval)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-timer.C:
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -1,185 +0,0 @@
|
|||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
// TimesStat contains the amounts of time the CPU has spent performing different
|
||||
// kinds of work. Time units are in seconds. It is based on linux /proc/stat file.
|
||||
type TimesStat struct {
|
||||
CPU string `json:"cpu"`
|
||||
User float64 `json:"user"`
|
||||
System float64 `json:"system"`
|
||||
Idle float64 `json:"idle"`
|
||||
Nice float64 `json:"nice"`
|
||||
Iowait float64 `json:"iowait"`
|
||||
Irq float64 `json:"irq"`
|
||||
Softirq float64 `json:"softirq"`
|
||||
Steal float64 `json:"steal"`
|
||||
Guest float64 `json:"guest"`
|
||||
GuestNice float64 `json:"guestNice"`
|
||||
}
|
||||
|
||||
type InfoStat struct {
|
||||
CPU int32 `json:"cpu"`
|
||||
VendorID string `json:"vendorId"`
|
||||
Family string `json:"family"`
|
||||
Model string `json:"model"`
|
||||
Stepping int32 `json:"stepping"`
|
||||
PhysicalID string `json:"physicalId"`
|
||||
CoreID string `json:"coreId"`
|
||||
Cores int32 `json:"cores"`
|
||||
ModelName string `json:"modelName"`
|
||||
Mhz float64 `json:"mhz"`
|
||||
CacheSize int32 `json:"cacheSize"`
|
||||
Flags []string `json:"flags"`
|
||||
Microcode string `json:"microcode"`
|
||||
}
|
||||
|
||||
type lastPercent struct {
|
||||
sync.Mutex
|
||||
lastCPUTimes []TimesStat
|
||||
lastPerCPUTimes []TimesStat
|
||||
}
|
||||
|
||||
var lastCPUPercent lastPercent
|
||||
var invoke common.Invoker = common.Invoke{}
|
||||
|
||||
func init() {
|
||||
lastCPUPercent.Lock()
|
||||
lastCPUPercent.lastCPUTimes, _ = Times(false)
|
||||
lastCPUPercent.lastPerCPUTimes, _ = Times(true)
|
||||
lastCPUPercent.Unlock()
|
||||
}
|
||||
|
||||
// Counts returns the number of physical or logical cores in the system
|
||||
func Counts(logical bool) (int, error) {
|
||||
return CountsWithContext(context.Background(), logical)
|
||||
}
|
||||
|
||||
func (c TimesStat) String() string {
|
||||
v := []string{
|
||||
`"cpu":"` + c.CPU + `"`,
|
||||
`"user":` + strconv.FormatFloat(c.User, 'f', 1, 64),
|
||||
`"system":` + strconv.FormatFloat(c.System, 'f', 1, 64),
|
||||
`"idle":` + strconv.FormatFloat(c.Idle, 'f', 1, 64),
|
||||
`"nice":` + strconv.FormatFloat(c.Nice, 'f', 1, 64),
|
||||
`"iowait":` + strconv.FormatFloat(c.Iowait, 'f', 1, 64),
|
||||
`"irq":` + strconv.FormatFloat(c.Irq, 'f', 1, 64),
|
||||
`"softirq":` + strconv.FormatFloat(c.Softirq, 'f', 1, 64),
|
||||
`"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64),
|
||||
`"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64),
|
||||
`"guestNice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64),
|
||||
}
|
||||
|
||||
return `{` + strings.Join(v, ",") + `}`
|
||||
}
|
||||
|
||||
// Total returns the total number of seconds in a CPUTimesStat
|
||||
func (c TimesStat) Total() float64 {
|
||||
total := c.User + c.System + c.Nice + c.Iowait + c.Irq + c.Softirq +
|
||||
c.Steal + c.Idle
|
||||
return total
|
||||
}
|
||||
|
||||
func (c InfoStat) String() string {
|
||||
s, _ := json.Marshal(c)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func getAllBusy(t TimesStat) (float64, float64) {
|
||||
busy := t.User + t.System + t.Nice + t.Iowait + t.Irq +
|
||||
t.Softirq + t.Steal
|
||||
return busy + t.Idle, busy
|
||||
}
|
||||
|
||||
func calculateBusy(t1, t2 TimesStat) float64 {
|
||||
t1All, t1Busy := getAllBusy(t1)
|
||||
t2All, t2Busy := getAllBusy(t2)
|
||||
|
||||
if t2Busy <= t1Busy {
|
||||
return 0
|
||||
}
|
||||
if t2All <= t1All {
|
||||
return 100
|
||||
}
|
||||
return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100))
|
||||
}
|
||||
|
||||
func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) {
|
||||
// Make sure the CPU measurements have the same length.
|
||||
if len(t1) != len(t2) {
|
||||
return nil, fmt.Errorf(
|
||||
"received two CPU counts: %d != %d",
|
||||
len(t1), len(t2),
|
||||
)
|
||||
}
|
||||
|
||||
ret := make([]float64, len(t1))
|
||||
for i, t := range t2 {
|
||||
ret[i] = calculateBusy(t1[i], t)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Percent calculates the percentage of cpu used either per CPU or combined.
|
||||
// If an interval of 0 is given it will compare the current cpu times against the last call.
|
||||
// Returns one value per cpu, or a single value if percpu is set to false.
|
||||
func Percent(interval time.Duration, percpu bool) ([]float64, error) {
|
||||
return PercentWithContext(context.Background(), interval, percpu)
|
||||
}
|
||||
|
||||
func PercentWithContext(ctx context.Context, interval time.Duration, percpu bool) ([]float64, error) {
|
||||
if interval <= 0 {
|
||||
return percentUsedFromLastCall(percpu)
|
||||
}
|
||||
|
||||
// Get CPU usage at the start of the interval.
|
||||
cpuTimes1, err := Times(percpu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := common.Sleep(ctx, interval); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// And at the end of the interval.
|
||||
cpuTimes2, err := Times(percpu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return calculateAllBusy(cpuTimes1, cpuTimes2)
|
||||
}
|
||||
|
||||
func percentUsedFromLastCall(percpu bool) ([]float64, error) {
|
||||
cpuTimes, err := Times(percpu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastCPUPercent.Lock()
|
||||
defer lastCPUPercent.Unlock()
|
||||
var lastTimes []TimesStat
|
||||
if percpu {
|
||||
lastTimes = lastCPUPercent.lastPerCPUTimes
|
||||
lastCPUPercent.lastPerCPUTimes = cpuTimes
|
||||
} else {
|
||||
lastTimes = lastCPUPercent.lastCPUTimes
|
||||
lastCPUPercent.lastCPUTimes = cpuTimes
|
||||
}
|
||||
|
||||
if lastTimes == nil {
|
||||
return nil, fmt.Errorf("error getting times for cpu percent. lastTimes was nil")
|
||||
}
|
||||
return calculateAllBusy(lastTimes, cpuTimes)
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// sys/resource.h
|
||||
const (
|
||||
CPUser = 0
|
||||
CPNice = 1
|
||||
CPSys = 2
|
||||
CPIntr = 3
|
||||
CPIdle = 4
|
||||
CPUStates = 5
|
||||
)
|
||||
|
||||
// default value. from time.h
|
||||
var ClocksPerSec = float64(128)
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("getconf")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out, err := invoke.Command(getconf, "CLK_TCK")
|
||||
// ignore errors
|
||||
if err == nil {
|
||||
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
|
||||
if err == nil {
|
||||
ClocksPerSec = float64(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Times(percpu bool) ([]TimesStat, error) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
if percpu {
|
||||
return perCPUTimes()
|
||||
}
|
||||
|
||||
return allCPUTimes()
|
||||
}
|
||||
|
||||
// Returns only one CPUInfoStat on FreeBSD
|
||||
func Info() ([]InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
var ret []InfoStat
|
||||
|
||||
c := InfoStat{}
|
||||
c.ModelName, _ = unix.Sysctl("machdep.cpu.brand_string")
|
||||
family, _ := unix.SysctlUint32("machdep.cpu.family")
|
||||
c.Family = strconv.FormatUint(uint64(family), 10)
|
||||
model, _ := unix.SysctlUint32("machdep.cpu.model")
|
||||
c.Model = strconv.FormatUint(uint64(model), 10)
|
||||
stepping, _ := unix.SysctlUint32("machdep.cpu.stepping")
|
||||
c.Stepping = int32(stepping)
|
||||
features, err := unix.Sysctl("machdep.cpu.features")
|
||||
if err == nil {
|
||||
for _, v := range strings.Fields(features) {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
leaf7Features, err := unix.Sysctl("machdep.cpu.leaf7_features")
|
||||
if err == nil {
|
||||
for _, v := range strings.Fields(leaf7Features) {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
extfeatures, err := unix.Sysctl("machdep.cpu.extfeatures")
|
||||
if err == nil {
|
||||
for _, v := range strings.Fields(extfeatures) {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
cores, _ := unix.SysctlUint32("machdep.cpu.core_count")
|
||||
c.Cores = int32(cores)
|
||||
cacheSize, _ := unix.SysctlUint32("machdep.cpu.cache.size")
|
||||
c.CacheSize = int32(cacheSize)
|
||||
c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor")
|
||||
|
||||
// Use the rated frequency of the CPU. This is a static value and does not
|
||||
// account for low power or Turbo Boost modes.
|
||||
cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
c.Mhz = float64(cpuFrequency) / 1000000.0
|
||||
|
||||
return append(ret, c), nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
var cpuArgument string
|
||||
if logical {
|
||||
cpuArgument = "hw.logicalcpu"
|
||||
} else {
|
||||
cpuArgument = "hw.physicalcpu"
|
||||
}
|
||||
|
||||
count, err := unix.SysctlUint32(cpuArgument)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(count), nil
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
//go:build darwin && cgo
|
||||
// +build darwin,cgo
|
||||
|
||||
package cpu
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/host_info.h>
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_OS_MAC
|
||||
#include <libproc.h>
|
||||
#endif
|
||||
#include <mach/processor_info.h>
|
||||
#include <mach/vm_map.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// these CPU times for darwin is borrowed from influxdb/telegraf.
|
||||
|
||||
func perCPUTimes() ([]TimesStat, error) {
|
||||
var (
|
||||
count C.mach_msg_type_number_t
|
||||
cpuload *C.processor_cpu_load_info_data_t
|
||||
ncpu C.natural_t
|
||||
)
|
||||
|
||||
status := C.host_processor_info(C.host_t(C.mach_host_self()),
|
||||
C.PROCESSOR_CPU_LOAD_INFO,
|
||||
&ncpu,
|
||||
(*C.processor_info_array_t)(unsafe.Pointer(&cpuload)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_processor_info error=%d", status)
|
||||
}
|
||||
|
||||
// jump through some cgo casting hoops and ensure we properly free
|
||||
// the memory that cpuload points to
|
||||
target := C.vm_map_t(C.mach_task_self_)
|
||||
address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload)))
|
||||
defer C.vm_deallocate(target, address, C.vm_size_t(ncpu))
|
||||
|
||||
// the body of struct processor_cpu_load_info
|
||||
// aka processor_cpu_load_info_data_t
|
||||
var cpu_ticks [C.CPU_STATE_MAX]uint32
|
||||
|
||||
// copy the cpuload array to a []byte buffer
|
||||
// where we can binary.Read the data
|
||||
size := int(ncpu) * binary.Size(cpu_ticks)
|
||||
buf := (*[1 << 30]byte)(unsafe.Pointer(cpuload))[:size:size]
|
||||
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
|
||||
var ret []TimesStat
|
||||
|
||||
for i := 0; i < int(ncpu); i++ {
|
||||
err := binary.Read(bbuf, binary.LittleEndian, &cpu_ticks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := TimesStat{
|
||||
CPU: fmt.Sprintf("cpu%d", i),
|
||||
User: float64(cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec,
|
||||
System: float64(cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec,
|
||||
Nice: float64(cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec,
|
||||
Idle: float64(cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec,
|
||||
}
|
||||
|
||||
ret = append(ret, c)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func allCPUTimes() ([]TimesStat, error) {
|
||||
var count C.mach_msg_type_number_t
|
||||
var cpuload C.host_cpu_load_info_data_t
|
||||
|
||||
count = C.HOST_CPU_LOAD_INFO_COUNT
|
||||
|
||||
status := C.host_statistics(C.host_t(C.mach_host_self()),
|
||||
C.HOST_CPU_LOAD_INFO,
|
||||
C.host_info_t(unsafe.Pointer(&cpuload)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_statistics error=%d", status)
|
||||
}
|
||||
|
||||
c := TimesStat{
|
||||
CPU: "cpu-total",
|
||||
User: float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec,
|
||||
System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec,
|
||||
Nice: float64(cpuload.cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec,
|
||||
Idle: float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec,
|
||||
}
|
||||
|
||||
return []TimesStat{c}, nil
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
//go:build darwin && !cgo
|
||||
// +build darwin,!cgo
|
||||
|
||||
package cpu
|
||||
|
||||
import "github.com/gofiber/fiber/v3/internal/gopsutil/common"
|
||||
|
||||
func perCPUTimes() ([]TimesStat, error) {
|
||||
return []TimesStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func allCPUTimes() ([]TimesStat, error) {
|
||||
return []TimesStat{}, common.ErrNotImplementedError
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gofiber/fiber/v3/internal/gopsutil/common"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var ClocksPerSec = float64(128)
|
||||
var cpuMatch = regexp.MustCompile(`^CPU:`)
|
||||
var originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`)
|
||||
var featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`)
|
||||
var featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`)
|
||||
var cpuEnd = regexp.MustCompile(`^Trying to mount root`)
|
||||
var cpuTimesSize int
|
||||
var emptyTimes cpuTimes
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("getconf")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out, err := invoke.Command(getconf, "CLK_TCK")
|
||||
// ignore errors
|
||||
if err == nil {
|
||||
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
|
||||
if err == nil {
|
||||
ClocksPerSec = float64(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func timeStat(name string, t *cpuTimes) *TimesStat {
|
||||
return &TimesStat{
|
||||
User: float64(t.User) / ClocksPerSec,
|
||||
Nice: float64(t.Nice) / ClocksPerSec,
|
||||
System: float64(t.Sys) / ClocksPerSec,
|
||||
Idle: float64(t.Idle) / ClocksPerSec,
|
||||
Irq: float64(t.Intr) / ClocksPerSec,
|
||||
CPU: name,
|
||||
}
|
||||
}
|
||||
|
||||
func Times(percpu bool) ([]TimesStat, error) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
if percpu {
|
||||
buf, err := unix.SysctlRaw("kern.cp_times")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We can't do this in init due to the conflict with cpu.init()
|
||||
if cpuTimesSize == 0 {
|
||||
cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size())
|
||||
}
|
||||
|
||||
ncpus := len(buf) / cpuTimesSize
|
||||
ret := make([]TimesStat, 0, ncpus)
|
||||
for i := 0; i < ncpus; i++ {
|
||||
times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize]))
|
||||
if *times == emptyTimes {
|
||||
// CPU not present
|
||||
continue
|
||||
}
|
||||
ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times))
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
buf, err := unix.SysctlRaw("kern.cp_time")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
times := (*cpuTimes)(unsafe.Pointer(&buf[0]))
|
||||
return []TimesStat{*timeStat("cpu-total", times)}, nil
|
||||
}
|
||||
|
||||
// Returns only one InfoStat on DragonflyBSD. The information regarding core
|
||||
// count, however is accurate and it is assumed that all InfoStat attributes
|
||||
// are the same across CPUs.
|
||||
func Info() ([]InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
const dmesgBoot = "/var/run/dmesg.boot"
|
||||
|
||||
c, err := parseDmesgBoot(dmesgBoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var u32 uint32
|
||||
if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Mhz = float64(u32)
|
||||
|
||||
var num int
|
||||
var buf string
|
||||
if buf, err = unix.Sysctl("hw.cpu_topology.tree"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
num = strings.Count(buf, "CHIP")
|
||||
c.Cores = int32(strings.Count(string(buf), "CORE") / num)
|
||||
|
||||
if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]InfoStat, num)
|
||||
for i := 0; i < num; i++ {
|
||||
ret[i] = c
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func parseDmesgBoot(fileName string) (InfoStat, error) {
|
||||
c := InfoStat{}
|
||||
lines, _ := common.ReadLines(fileName)
|
||||
for _, line := range lines {
|
||||
if matches := cpuEnd.FindStringSubmatch(line); matches != nil {
|
||||
break
|
||||
} else if matches := originMatch.FindStringSubmatch(line); matches != nil {
|
||||
c.VendorID = matches[1]
|
||||
t, err := strconv.ParseInt(matches[2], 10, 32)
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("unable to parse DragonflyBSD CPU stepping information from %q: %v", line, err)
|
||||
}
|
||||
c.Stepping = int32(t)
|
||||
} else if matches := featuresMatch.FindStringSubmatch(line); matches != nil {
|
||||
for _, v := range strings.Split(matches[1], ",") {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
} else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil {
|
||||
for _, v := range strings.Split(matches[1], ",") {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue