feat(wallet): add collectibles filter option tokenIDs
This commit is contained in:
parent
ea3e59ffee
commit
b38e2c9278
3
go.mod
3
go.mod
|
@ -101,6 +101,7 @@ require (
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||||
|
github.com/Masterminds/squirrel v1.5.4 // indirect
|
||||||
github.com/PuerkitoBio/goquery v1.6.1 // indirect
|
github.com/PuerkitoBio/goquery v1.6.1 // indirect
|
||||||
github.com/RoaringBitmap/roaring v0.9.4 // indirect
|
github.com/RoaringBitmap/roaring v0.9.4 // indirect
|
||||||
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
|
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
|
||||||
|
@ -177,6 +178,8 @@ require (
|
||||||
github.com/klauspost/compress v1.16.7 // indirect
|
github.com/klauspost/compress v1.16.7 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||||
github.com/koron/go-ssdp v0.0.4 // indirect
|
github.com/koron/go-ssdp v0.0.4 // indirect
|
||||||
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||||
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||||
github.com/libp2p/go-cidranger v1.1.0 // indirect
|
github.com/libp2p/go-cidranger v1.1.0 // indirect
|
||||||
|
|
7
go.sum
7
go.sum
|
@ -111,6 +111,8 @@ github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHg
|
||||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
|
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
|
||||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||||
|
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
|
||||||
|
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
||||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||||
|
@ -820,6 +822,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
|
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||||
|
@ -1324,6 +1327,10 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||||
github.com/ladydascalie/currency v1.6.0 h1:r5s/TMCYcpn6jPRHLV3F8nI7YjpY8trvstfuixxiHns=
|
github.com/ladydascalie/currency v1.6.0 h1:r5s/TMCYcpn6jPRHLV3F8nI7YjpY8trvstfuixxiHns=
|
||||||
github.com/ladydascalie/currency v1.6.0/go.mod h1:C9eil8e6tthhBb5yhwoH1U0LT5hm1BP/g+v/V82KYjY=
|
github.com/ladydascalie/currency v1.6.0/go.mod h1:C9eil8e6tthhBb5yhwoH1U0LT5hm1BP/g+v/V82KYjY=
|
||||||
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
||||||
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||||
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||||
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
|
||||||
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
||||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
|
|
|
@ -5,18 +5,20 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
// used for embedding the sql query in the binary
|
|
||||||
_ "embed"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
|
||||||
"github.com/status-im/status-go/protocol/communities/token"
|
"github.com/status-im/status-go/protocol/communities/token"
|
||||||
|
"github.com/status-im/status-go/services/wallet/bigint"
|
||||||
wcommon "github.com/status-im/status-go/services/wallet/common"
|
wcommon "github.com/status-im/status-go/services/wallet/common"
|
||||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func allCollectibleIDsFilter() []thirdparty.CollectibleUniqueID {
|
||||||
|
return []thirdparty.CollectibleUniqueID{}
|
||||||
|
}
|
||||||
|
|
||||||
func allCommunityIDsFilter() []string {
|
func allCommunityIDsFilter() []string {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +29,7 @@ func allCommunityPrivilegesLevelsFilter() []token.PrivilegesLevel {
|
||||||
|
|
||||||
func allFilter() Filter {
|
func allFilter() Filter {
|
||||||
return Filter{
|
return Filter{
|
||||||
|
CollectibleIDs: allCollectibleIDsFilter(),
|
||||||
CommunityIDs: allCommunityIDsFilter(),
|
CommunityIDs: allCommunityIDsFilter(),
|
||||||
CommunityPrivilegesLevels: allCommunityPrivilegesLevelsFilter(),
|
CommunityPrivilegesLevels: allCommunityPrivilegesLevelsFilter(),
|
||||||
FilterCommunity: All,
|
FilterCommunity: All,
|
||||||
|
@ -42,15 +45,13 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
CommunityIDs []string `json:"community_ids"`
|
CollectibleIDs []thirdparty.CollectibleUniqueID `json:"collectible_ids"`
|
||||||
CommunityPrivilegesLevels []token.PrivilegesLevel `json:"community_privileges_levels"`
|
CommunityIDs []string `json:"community_ids"`
|
||||||
|
CommunityPrivilegesLevels []token.PrivilegesLevel `json:"community_privileges_levels"`
|
||||||
|
|
||||||
FilterCommunity FilterCommunityType `json:"filter_community"`
|
FilterCommunity FilterCommunityType `json:"filter_community"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed filter.sql
|
|
||||||
var queryString string
|
|
||||||
|
|
||||||
func filterOwnedCollectibles(ctx context.Context, db *sql.DB, chainIDs []wcommon.ChainID, addresses []common.Address, filter Filter, offset int, limit int) ([]thirdparty.CollectibleUniqueID, error) {
|
func filterOwnedCollectibles(ctx context.Context, db *sql.DB, chainIDs []wcommon.ChainID, addresses []common.Address, filter Filter, offset int, limit int) ([]thirdparty.CollectibleUniqueID, error) {
|
||||||
if len(addresses) == 0 {
|
if len(addresses) == 0 {
|
||||||
return nil, errors.New("no addresses provided")
|
return nil, errors.New("no addresses provided")
|
||||||
|
@ -59,25 +60,53 @@ func filterOwnedCollectibles(ctx context.Context, db *sql.DB, chainIDs []wcommon
|
||||||
return nil, errors.New("no chainIDs provided")
|
return nil, errors.New("no chainIDs provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
filterCommunityTypeAll := filter.FilterCommunity == All
|
q := sq.Select("ownership.chain_id,ownership.contract_address,ownership.token_id")
|
||||||
filterCommunityTypeOnlyNonCommunity := filter.FilterCommunity == OnlyNonCommunity
|
q = q.From("collectibles_ownership_cache ownership").
|
||||||
filterCommunityTypeOnlyCommunity := filter.FilterCommunity == OnlyCommunity
|
LeftJoin(`collectible_data_cache data ON
|
||||||
communityIDFilterDisabled := len(filter.CommunityIDs) == 0
|
ownership.chain_id = data.chain_id AND
|
||||||
if communityIDFilterDisabled {
|
ownership.contract_address = data.contract_address AND
|
||||||
// IN clause doesn't work with empty array, so we need to provide a dummy value
|
ownership.token_id = data.token_id`)
|
||||||
filter.CommunityIDs = []string{""}
|
|
||||||
}
|
qConditions := sq.And{}
|
||||||
communityPrivilegesLevelDisabled := len(filter.CommunityPrivilegesLevels) == 0
|
qConditions = append(qConditions, sq.Eq{"ownership.chain_id": chainIDs})
|
||||||
if communityPrivilegesLevelDisabled {
|
qConditions = append(qConditions, sq.Eq{"ownership.owner_address": addresses})
|
||||||
// IN clause doesn't work with empty array, so we need to provide a dummy value
|
|
||||||
filter.CommunityPrivilegesLevels = []token.PrivilegesLevel{token.PrivilegesLevel(0)}
|
if len(filter.CollectibleIDs) > 0 {
|
||||||
|
collectibleIDConditions := sq.Or{}
|
||||||
|
for _, collectibleID := range filter.CollectibleIDs {
|
||||||
|
collectibleIDConditions = append(collectibleIDConditions,
|
||||||
|
sq.And{
|
||||||
|
sq.Eq{"ownership.chain_id": collectibleID.ContractID.ChainID},
|
||||||
|
sq.Eq{"ownership.contract_address": collectibleID.ContractID.Address},
|
||||||
|
sq.Eq{"ownership.token_id": (*bigint.SQLBigIntBytes)(collectibleID.TokenID.Int)},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
qConditions = append(qConditions, collectibleIDConditions)
|
||||||
}
|
}
|
||||||
|
|
||||||
query, args, err := sqlx.In(queryString,
|
switch filter.FilterCommunity {
|
||||||
filterCommunityTypeAll, filterCommunityTypeOnlyNonCommunity, filterCommunityTypeOnlyCommunity,
|
case All:
|
||||||
communityIDFilterDisabled, communityPrivilegesLevelDisabled,
|
// nothing to do
|
||||||
chainIDs, addresses, filter.CommunityIDs, filter.CommunityPrivilegesLevels,
|
case OnlyNonCommunity:
|
||||||
limit, offset)
|
qConditions = append(qConditions, sq.Eq{"data.community_id": ""})
|
||||||
|
case OnlyCommunity:
|
||||||
|
qConditions = append(qConditions, sq.NotEq{"data.community_id": ""})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filter.CommunityIDs) > 0 {
|
||||||
|
qConditions = append(qConditions, sq.Eq{"data.community_id": filter.CommunityIDs})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filter.CommunityPrivilegesLevels) > 0 {
|
||||||
|
qConditions = append(qConditions, sq.Eq{"data.community_privileges_level": filter.CommunityPrivilegesLevels})
|
||||||
|
}
|
||||||
|
|
||||||
|
q = q.Where(qConditions)
|
||||||
|
|
||||||
|
q = q.Limit(uint64(limit))
|
||||||
|
q = q.Offset(uint64(offset))
|
||||||
|
|
||||||
|
query, args, err := q.ToSql()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
WITH filter_conditions AS (
|
|
||||||
SELECT
|
|
||||||
? AS filterCommunityTypeAll,
|
|
||||||
? AS filterCommunityTypeOnlyNonCommunity,
|
|
||||||
? AS filterCommunityTypeOnlyCommunity,
|
|
||||||
? AS communityIDFilterDisabled,
|
|
||||||
? AS communityPrivilegesLevelDisabled
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
ownership.chain_id,
|
|
||||||
ownership.contract_address,
|
|
||||||
ownership.token_id
|
|
||||||
FROM
|
|
||||||
collectibles_ownership_cache ownership
|
|
||||||
LEFT JOIN collectible_data_cache data
|
|
||||||
ON (
|
|
||||||
ownership.chain_id = data.chain_id
|
|
||||||
AND ownership.contract_address = data.contract_address
|
|
||||||
AND ownership.token_id = data.token_id
|
|
||||||
)
|
|
||||||
CROSS JOIN filter_conditions
|
|
||||||
WHERE
|
|
||||||
ownership.chain_id IN (?)
|
|
||||||
AND ownership.owner_address IN (?)
|
|
||||||
AND (
|
|
||||||
filterCommunityTypeAll
|
|
||||||
OR (
|
|
||||||
filterCommunityTypeOnlyNonCommunity
|
|
||||||
AND data.community_id = ""
|
|
||||||
)
|
|
||||||
OR (
|
|
||||||
filterCommunityTypeOnlyCommunity
|
|
||||||
AND data.community_id <> ""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
AND (
|
|
||||||
communityIDFilterDisabled
|
|
||||||
OR (
|
|
||||||
data.community_id IN (?)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
AND (
|
|
||||||
communityPrivilegesLevelDisabled
|
|
||||||
OR (
|
|
||||||
data.community_privileges_level IN (?)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
LIMIT
|
|
||||||
? OFFSET ?
|
|
|
@ -192,4 +192,26 @@ func TestFilterOwnedCollectibles(t *testing.T) {
|
||||||
filterIDs, err = filterOwnedCollectibles(ctx, db, filterChains, filterAddresses, filter, 0, nData)
|
filterIDs, err = filterOwnedCollectibles(ctx, db, filterChains, filterAddresses, filter, 0, nData)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expectedIDs, filterIDs)
|
require.Equal(t, expectedIDs, filterIDs)
|
||||||
|
|
||||||
|
// Test specific collectible IDs
|
||||||
|
tmpIDs, err = oDB.GetOwnedCollectibles(filterChains, filterAddresses, 0, nData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
filter = allFilter()
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
filter.CollectibleIDs = append(filter.CollectibleIDs, tmpIDs[i*2])
|
||||||
|
}
|
||||||
|
expectedIDs = filter.CollectibleIDs
|
||||||
|
|
||||||
|
filter.CollectibleIDs = append(filter.CollectibleIDs, thirdparty.CollectibleUniqueID{
|
||||||
|
ContractID: thirdparty.ContractID{
|
||||||
|
ChainID: w_common.ChainID(1),
|
||||||
|
Address: common.HexToAddress("0x1234"),
|
||||||
|
},
|
||||||
|
TokenID: &bigint.BigInt{Int: big.NewInt(9999999)},
|
||||||
|
})
|
||||||
|
|
||||||
|
filterIDs, err = filterOwnedCollectibles(ctx, db, filterChains, filterAddresses, filter, 0, nData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedIDs, filterIDs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
squirrel.test
|
|
@ -0,0 +1,30 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.11.x
|
||||||
|
- 1.12.x
|
||||||
|
- 1.13.x
|
||||||
|
|
||||||
|
services:
|
||||||
|
- mysql
|
||||||
|
- postgresql
|
||||||
|
|
||||||
|
# Setting sudo access to false will let Travis CI use containers rather than
|
||||||
|
# VMs to run the tests. For more details see:
|
||||||
|
# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||||
|
# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- mysql -e 'CREATE DATABASE squirrel;'
|
||||||
|
- psql -c 'CREATE DATABASE squirrel;' -U postgres
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test
|
||||||
|
- cd integration
|
||||||
|
- go test -args -driver sqlite3
|
||||||
|
- go test -args -driver mysql -dataSource travis@/squirrel
|
||||||
|
- go test -args -driver postgres -dataSource 'postgres://postgres@localhost/squirrel?sslmode=disable'
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
irc: "irc.freenode.net#masterminds"
|
|
@ -0,0 +1,23 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Squirrel: The Masterminds
|
||||||
|
Copyright (c) 2014-2015, Lann Martin. Copyright (C) 2015-2016, Google. Copyright (C) 2015, Matt Farina and Matt Butcher.
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,142 @@
|
||||||
|
[![Stability: Maintenance](https://masterminds.github.io/stability/maintenance.svg)](https://masterminds.github.io/stability/maintenance.html)
|
||||||
|
### Squirrel is "complete".
|
||||||
|
Bug fixes will still be merged (slowly). Bug reports are welcome, but I will not necessarily respond to them. If another fork (or substantially similar project) actively improves on what Squirrel does, let me know and I may link to it here.
|
||||||
|
|
||||||
|
|
||||||
|
# Squirrel - fluent SQL generator for Go
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/Masterminds/squirrel"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
[![GoDoc](https://godoc.org/github.com/Masterminds/squirrel?status.png)](https://godoc.org/github.com/Masterminds/squirrel)
|
||||||
|
[![Build Status](https://api.travis-ci.org/Masterminds/squirrel.svg?branch=master)](https://travis-ci.org/Masterminds/squirrel)
|
||||||
|
|
||||||
|
**Squirrel is not an ORM.** For an application of Squirrel, check out
|
||||||
|
[structable, a table-struct mapper](https://github.com/Masterminds/structable)
|
||||||
|
|
||||||
|
|
||||||
|
Squirrel helps you build SQL queries from composable parts:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import sq "github.com/Masterminds/squirrel"
|
||||||
|
|
||||||
|
users := sq.Select("*").From("users").Join("emails USING (email_id)")
|
||||||
|
|
||||||
|
active := users.Where(sq.Eq{"deleted_at": nil})
|
||||||
|
|
||||||
|
sql, args, err := active.ToSql()
|
||||||
|
|
||||||
|
sql == "SELECT * FROM users JOIN emails USING (email_id) WHERE deleted_at IS NULL"
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
sql, args, err := sq.
|
||||||
|
Insert("users").Columns("name", "age").
|
||||||
|
Values("moe", 13).Values("larry", sq.Expr("? + 5", 12)).
|
||||||
|
ToSql()
|
||||||
|
|
||||||
|
sql == "INSERT INTO users (name,age) VALUES (?,?),(?,? + 5)"
|
||||||
|
```
|
||||||
|
|
||||||
|
Squirrel can also execute queries directly:
|
||||||
|
|
||||||
|
```go
|
||||||
|
stooges := users.Where(sq.Eq{"username": []string{"moe", "larry", "curly", "shemp"}})
|
||||||
|
three_stooges := stooges.Limit(3)
|
||||||
|
rows, err := three_stooges.RunWith(db).Query()
|
||||||
|
|
||||||
|
// Behaves like:
|
||||||
|
rows, err := db.Query("SELECT * FROM users WHERE username IN (?,?,?,?) LIMIT 3",
|
||||||
|
"moe", "larry", "curly", "shemp")
|
||||||
|
```
|
||||||
|
|
||||||
|
Squirrel makes conditional query building a breeze:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if len(q) > 0 {
|
||||||
|
users = users.Where("name LIKE ?", fmt.Sprint("%", q, "%"))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Squirrel wants to make your life easier:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// StmtCache caches Prepared Stmts for you
|
||||||
|
dbCache := sq.NewStmtCache(db)
|
||||||
|
|
||||||
|
// StatementBuilder keeps your syntax neat
|
||||||
|
mydb := sq.StatementBuilder.RunWith(dbCache)
|
||||||
|
select_users := mydb.Select("*").From("users")
|
||||||
|
```
|
||||||
|
|
||||||
|
Squirrel loves PostgreSQL:
|
||||||
|
|
||||||
|
```go
|
||||||
|
psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)
|
||||||
|
|
||||||
|
// You use question marks for placeholders...
|
||||||
|
sql, _, _ := psql.Select("*").From("elephants").Where("name IN (?,?)", "Dumbo", "Verna").ToSql()
|
||||||
|
|
||||||
|
/// ...squirrel replaces them using PlaceholderFormat.
|
||||||
|
sql == "SELECT * FROM elephants WHERE name IN ($1,$2)"
|
||||||
|
|
||||||
|
|
||||||
|
/// You can retrieve id ...
|
||||||
|
query := sq.Insert("nodes").
|
||||||
|
Columns("uuid", "type", "data").
|
||||||
|
Values(node.Uuid, node.Type, node.Data).
|
||||||
|
Suffix("RETURNING \"id\"").
|
||||||
|
RunWith(m.db).
|
||||||
|
PlaceholderFormat(sq.Dollar)
|
||||||
|
|
||||||
|
query.QueryRow().Scan(&node.id)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can escape question marks by inserting two question marks:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM nodes WHERE meta->'format' ??| array[?,?]
|
||||||
|
```
|
||||||
|
|
||||||
|
will generate with the Dollar Placeholder:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM nodes WHERE meta->'format' ?| array[$1,$2]
|
||||||
|
```
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
* **How can I build an IN query on composite keys / tuples, e.g. `WHERE (col1, col2) IN ((1,2),(3,4))`? ([#104](https://github.com/Masterminds/squirrel/issues/104))**
|
||||||
|
|
||||||
|
Squirrel does not explicitly support tuples, but you can get the same effect with e.g.:
|
||||||
|
|
||||||
|
```go
|
||||||
|
sq.Or{
|
||||||
|
sq.Eq{"col1": 1, "col2": 2},
|
||||||
|
sq.Eq{"col1": 3, "col2": 4}}
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql
|
||||||
|
WHERE (col1 = 1 AND col2 = 2) OR (col1 = 3 AND col2 = 4)
|
||||||
|
```
|
||||||
|
|
||||||
|
(which should produce the same query plan as the tuple version)
|
||||||
|
|
||||||
|
* **Why doesn't `Eq{"mynumber": []uint8{1,2,3}}` turn into an `IN` query? ([#114](https://github.com/Masterminds/squirrel/issues/114))**
|
||||||
|
|
||||||
|
Values of type `[]byte` are handled specially by `database/sql`. In Go, [`byte` is just an alias of `uint8`](https://golang.org/pkg/builtin/#byte), so there is no way to distinguish `[]uint8` from `[]byte`.
|
||||||
|
|
||||||
|
* **Some features are poorly documented!**
|
||||||
|
|
||||||
|
This isn't a frequent complaints section!
|
||||||
|
|
||||||
|
* **Some features are poorly documented?**
|
||||||
|
|
||||||
|
Yes. The tests should be considered a part of the documentation; take a look at those for ideas on how to express more complex queries.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Squirrel is released under the
|
||||||
|
[MIT License](http://www.opensource.org/licenses/MIT).
|
|
@ -0,0 +1,128 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/lann/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
builder.Register(CaseBuilder{}, caseData{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// sqlizerBuffer is a helper that allows to write many Sqlizers one by one
|
||||||
|
// without constant checks for errors that may come from Sqlizer
|
||||||
|
type sqlizerBuffer struct {
|
||||||
|
bytes.Buffer
|
||||||
|
args []interface{}
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteSql converts Sqlizer to SQL strings and writes it to buffer
|
||||||
|
func (b *sqlizerBuffer) WriteSql(item Sqlizer) {
|
||||||
|
if b.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var str string
|
||||||
|
var args []interface{}
|
||||||
|
str, args, b.err = nestedToSql(item)
|
||||||
|
|
||||||
|
if b.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString(str)
|
||||||
|
b.WriteByte(' ')
|
||||||
|
b.args = append(b.args, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *sqlizerBuffer) ToSql() (string, []interface{}, error) {
|
||||||
|
return b.String(), b.args, b.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// whenPart is a helper structure to describe SQLs "WHEN ... THEN ..." expression
|
||||||
|
type whenPart struct {
|
||||||
|
when Sqlizer
|
||||||
|
then Sqlizer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWhenPart(when interface{}, then interface{}) whenPart {
|
||||||
|
return whenPart{newPart(when), newPart(then)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// caseData holds all the data required to build a CASE SQL construct
|
||||||
|
type caseData struct {
|
||||||
|
What Sqlizer
|
||||||
|
WhenParts []whenPart
|
||||||
|
Else Sqlizer
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSql implements Sqlizer
|
||||||
|
func (d *caseData) ToSql() (sqlStr string, args []interface{}, err error) {
|
||||||
|
if len(d.WhenParts) == 0 {
|
||||||
|
err = errors.New("case expression must contain at lease one WHEN clause")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sql := sqlizerBuffer{}
|
||||||
|
|
||||||
|
sql.WriteString("CASE ")
|
||||||
|
if d.What != nil {
|
||||||
|
sql.WriteSql(d.What)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range d.WhenParts {
|
||||||
|
sql.WriteString("WHEN ")
|
||||||
|
sql.WriteSql(p.when)
|
||||||
|
sql.WriteString("THEN ")
|
||||||
|
sql.WriteSql(p.then)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.Else != nil {
|
||||||
|
sql.WriteString("ELSE ")
|
||||||
|
sql.WriteSql(d.Else)
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.WriteString("END")
|
||||||
|
|
||||||
|
return sql.ToSql()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CaseBuilder builds SQL CASE construct which could be used as parts of queries.
|
||||||
|
type CaseBuilder builder.Builder
|
||||||
|
|
||||||
|
// ToSql builds the query into a SQL string and bound args.
|
||||||
|
func (b CaseBuilder) ToSql() (string, []interface{}, error) {
|
||||||
|
data := builder.GetStruct(b).(caseData)
|
||||||
|
return data.ToSql()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustSql builds the query into a SQL string and bound args.
|
||||||
|
// It panics if there are any errors.
|
||||||
|
func (b CaseBuilder) MustSql() (string, []interface{}) {
|
||||||
|
sql, args, err := b.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sql, args
|
||||||
|
}
|
||||||
|
|
||||||
|
// what sets optional value for CASE construct "CASE [value] ..."
|
||||||
|
func (b CaseBuilder) what(expr interface{}) CaseBuilder {
|
||||||
|
return builder.Set(b, "What", newPart(expr)).(CaseBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When adds "WHEN ... THEN ..." part to CASE construct
|
||||||
|
func (b CaseBuilder) When(when interface{}, then interface{}) CaseBuilder {
|
||||||
|
// TODO: performance hint: replace slice of WhenPart with just slice of parts
|
||||||
|
// where even indices of the slice belong to "when"s and odd indices belong to "then"s
|
||||||
|
return builder.Append(b, "WhenParts", newWhenPart(when, then)).(CaseBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// What sets optional "ELSE ..." part for CASE construct
|
||||||
|
func (b CaseBuilder) Else(expr interface{}) CaseBuilder {
|
||||||
|
return builder.Set(b, "Else", newPart(expr)).(CaseBuilder)
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/lann/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type deleteData struct {
|
||||||
|
PlaceholderFormat PlaceholderFormat
|
||||||
|
RunWith BaseRunner
|
||||||
|
Prefixes []Sqlizer
|
||||||
|
From string
|
||||||
|
WhereParts []Sqlizer
|
||||||
|
OrderBys []string
|
||||||
|
Limit string
|
||||||
|
Offset string
|
||||||
|
Suffixes []Sqlizer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deleteData) Exec() (sql.Result, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
return ExecWith(d.RunWith, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deleteData) ToSql() (sqlStr string, args []interface{}, err error) {
|
||||||
|
if len(d.From) == 0 {
|
||||||
|
err = fmt.Errorf("delete statements must specify a From table")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sql := &bytes.Buffer{}
|
||||||
|
|
||||||
|
if len(d.Prefixes) > 0 {
|
||||||
|
args, err = appendToSql(d.Prefixes, sql, " ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.WriteString("DELETE FROM ")
|
||||||
|
sql.WriteString(d.From)
|
||||||
|
|
||||||
|
if len(d.WhereParts) > 0 {
|
||||||
|
sql.WriteString(" WHERE ")
|
||||||
|
args, err = appendToSql(d.WhereParts, sql, " AND ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.OrderBys) > 0 {
|
||||||
|
sql.WriteString(" ORDER BY ")
|
||||||
|
sql.WriteString(strings.Join(d.OrderBys, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Limit) > 0 {
|
||||||
|
sql.WriteString(" LIMIT ")
|
||||||
|
sql.WriteString(d.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Offset) > 0 {
|
||||||
|
sql.WriteString(" OFFSET ")
|
||||||
|
sql.WriteString(d.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Suffixes) > 0 {
|
||||||
|
sql.WriteString(" ")
|
||||||
|
args, err = appendToSql(d.Suffixes, sql, " ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builder
|
||||||
|
|
||||||
|
// DeleteBuilder builds SQL DELETE statements.
|
||||||
|
type DeleteBuilder builder.Builder
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
builder.Register(DeleteBuilder{}, deleteData{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format methods
|
||||||
|
|
||||||
|
// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the
|
||||||
|
// query.
|
||||||
|
func (b DeleteBuilder) PlaceholderFormat(f PlaceholderFormat) DeleteBuilder {
|
||||||
|
return builder.Set(b, "PlaceholderFormat", f).(DeleteBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runner methods
|
||||||
|
|
||||||
|
// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec.
|
||||||
|
func (b DeleteBuilder) RunWith(runner BaseRunner) DeleteBuilder {
|
||||||
|
return setRunWith(b, runner).(DeleteBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec builds and Execs the query with the Runner set by RunWith.
|
||||||
|
func (b DeleteBuilder) Exec() (sql.Result, error) {
|
||||||
|
data := builder.GetStruct(b).(deleteData)
|
||||||
|
return data.Exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SQL methods
|
||||||
|
|
||||||
|
// ToSql builds the query into a SQL string and bound args.
|
||||||
|
func (b DeleteBuilder) ToSql() (string, []interface{}, error) {
|
||||||
|
data := builder.GetStruct(b).(deleteData)
|
||||||
|
return data.ToSql()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustSql builds the query into a SQL string and bound args.
|
||||||
|
// It panics if there are any errors.
|
||||||
|
func (b DeleteBuilder) MustSql() (string, []interface{}) {
|
||||||
|
sql, args, err := b.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sql, args
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix adds an expression to the beginning of the query
|
||||||
|
func (b DeleteBuilder) Prefix(sql string, args ...interface{}) DeleteBuilder {
|
||||||
|
return b.PrefixExpr(Expr(sql, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrefixExpr adds an expression to the very beginning of the query
|
||||||
|
func (b DeleteBuilder) PrefixExpr(expr Sqlizer) DeleteBuilder {
|
||||||
|
return builder.Append(b, "Prefixes", expr).(DeleteBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// From sets the table to be deleted from.
|
||||||
|
func (b DeleteBuilder) From(from string) DeleteBuilder {
|
||||||
|
return builder.Set(b, "From", from).(DeleteBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where adds WHERE expressions to the query.
|
||||||
|
//
|
||||||
|
// See SelectBuilder.Where for more information.
|
||||||
|
func (b DeleteBuilder) Where(pred interface{}, args ...interface{}) DeleteBuilder {
|
||||||
|
return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(DeleteBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderBy adds ORDER BY expressions to the query.
|
||||||
|
func (b DeleteBuilder) OrderBy(orderBys ...string) DeleteBuilder {
|
||||||
|
return builder.Extend(b, "OrderBys", orderBys).(DeleteBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit sets a LIMIT clause on the query.
|
||||||
|
func (b DeleteBuilder) Limit(limit uint64) DeleteBuilder {
|
||||||
|
return builder.Set(b, "Limit", fmt.Sprintf("%d", limit)).(DeleteBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset sets a OFFSET clause on the query.
|
||||||
|
func (b DeleteBuilder) Offset(offset uint64) DeleteBuilder {
|
||||||
|
return builder.Set(b, "Offset", fmt.Sprintf("%d", offset)).(DeleteBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suffix adds an expression to the end of the query
|
||||||
|
func (b DeleteBuilder) Suffix(sql string, args ...interface{}) DeleteBuilder {
|
||||||
|
return b.SuffixExpr(Expr(sql, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuffixExpr adds an expression to the end of the query
|
||||||
|
func (b DeleteBuilder) SuffixExpr(expr Sqlizer) DeleteBuilder {
|
||||||
|
return builder.Append(b, "Suffixes", expr).(DeleteBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b DeleteBuilder) Query() (*sql.Rows, error) {
|
||||||
|
data := builder.GetStruct(b).(deleteData)
|
||||||
|
return data.Query()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deleteData) Query() (*sql.Rows, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
return QueryWith(d.RunWith, d)
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/lann/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *deleteData) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
ctxRunner, ok := d.RunWith.(ExecerContext)
|
||||||
|
if !ok {
|
||||||
|
return nil, NoContextSupport
|
||||||
|
}
|
||||||
|
return ExecContextWith(ctx, ctxRunner, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deleteData) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
ctxRunner, ok := d.RunWith.(QueryerContext)
|
||||||
|
if !ok {
|
||||||
|
return nil, NoContextSupport
|
||||||
|
}
|
||||||
|
return QueryContextWith(ctx, ctxRunner, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deleteData) QueryRowContext(ctx context.Context) RowScanner {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return &Row{err: RunnerNotSet}
|
||||||
|
}
|
||||||
|
queryRower, ok := d.RunWith.(QueryRowerContext)
|
||||||
|
if !ok {
|
||||||
|
if _, ok := d.RunWith.(QueryerContext); !ok {
|
||||||
|
return &Row{err: RunnerNotQueryRunner}
|
||||||
|
}
|
||||||
|
return &Row{err: NoContextSupport}
|
||||||
|
}
|
||||||
|
return QueryRowContextWith(ctx, queryRower, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecContext builds and ExecContexts the query with the Runner set by RunWith.
|
||||||
|
func (b DeleteBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||||
|
data := builder.GetStruct(b).(deleteData)
|
||||||
|
return data.ExecContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryContext builds and QueryContexts the query with the Runner set by RunWith.
|
||||||
|
func (b DeleteBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||||
|
data := builder.GetStruct(b).(deleteData)
|
||||||
|
return data.QueryContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith.
|
||||||
|
func (b DeleteBuilder) QueryRowContext(ctx context.Context) RowScanner {
|
||||||
|
data := builder.GetStruct(b).(deleteData)
|
||||||
|
return data.QueryRowContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScanContext is a shortcut for QueryRowContext().Scan.
|
||||||
|
func (b DeleteBuilder) ScanContext(ctx context.Context, dest ...interface{}) error {
|
||||||
|
return b.QueryRowContext(ctx).Scan(dest...)
|
||||||
|
}
|
|
@ -0,0 +1,419 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Portable true/false literals.
|
||||||
|
sqlTrue = "(1=1)"
|
||||||
|
sqlFalse = "(1=0)"
|
||||||
|
)
|
||||||
|
|
||||||
|
type expr struct {
|
||||||
|
sql string
|
||||||
|
args []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expr builds an expression from a SQL fragment and arguments.
|
||||||
|
//
|
||||||
|
// Ex:
|
||||||
|
// Expr("FROM_UNIXTIME(?)", t)
|
||||||
|
func Expr(sql string, args ...interface{}) Sqlizer {
|
||||||
|
return expr{sql: sql, args: args}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e expr) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
simple := true
|
||||||
|
for _, arg := range e.args {
|
||||||
|
if _, ok := arg.(Sqlizer); ok {
|
||||||
|
simple = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if simple {
|
||||||
|
return e.sql, e.args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
ap := e.args
|
||||||
|
sp := e.sql
|
||||||
|
|
||||||
|
var isql string
|
||||||
|
var iargs []interface{}
|
||||||
|
|
||||||
|
for err == nil && len(ap) > 0 && len(sp) > 0 {
|
||||||
|
i := strings.Index(sp, "?")
|
||||||
|
if i < 0 {
|
||||||
|
// no more placeholders
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(sp) > i+1 && sp[i+1:i+2] == "?" {
|
||||||
|
// escaped "??"; append it and step past
|
||||||
|
buf.WriteString(sp[:i+2])
|
||||||
|
sp = sp[i+2:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if as, ok := ap[0].(Sqlizer); ok {
|
||||||
|
// sqlizer argument; expand it and append the result
|
||||||
|
isql, iargs, err = as.ToSql()
|
||||||
|
buf.WriteString(sp[:i])
|
||||||
|
buf.WriteString(isql)
|
||||||
|
args = append(args, iargs...)
|
||||||
|
} else {
|
||||||
|
// normal argument; append it and the placeholder
|
||||||
|
buf.WriteString(sp[:i+1])
|
||||||
|
args = append(args, ap[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// step past the argument and placeholder
|
||||||
|
ap = ap[1:]
|
||||||
|
sp = sp[i+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// append the remaining sql and arguments
|
||||||
|
buf.WriteString(sp)
|
||||||
|
return buf.String(), append(args, ap...), err
|
||||||
|
}
|
||||||
|
|
||||||
|
type concatExpr []interface{}
|
||||||
|
|
||||||
|
func (ce concatExpr) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
for _, part := range ce {
|
||||||
|
switch p := part.(type) {
|
||||||
|
case string:
|
||||||
|
sql += p
|
||||||
|
case Sqlizer:
|
||||||
|
pSql, pArgs, err := p.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
sql += pSql
|
||||||
|
args = append(args, pArgs...)
|
||||||
|
default:
|
||||||
|
return "", nil, fmt.Errorf("%#v is not a string or Sqlizer", part)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConcatExpr builds an expression by concatenating strings and other expressions.
|
||||||
|
//
|
||||||
|
// Ex:
|
||||||
|
// name_expr := Expr("CONCAT(?, ' ', ?)", firstName, lastName)
|
||||||
|
// ConcatExpr("COALESCE(full_name,", name_expr, ")")
|
||||||
|
func ConcatExpr(parts ...interface{}) concatExpr {
|
||||||
|
return concatExpr(parts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// aliasExpr helps to alias part of SQL query generated with underlying "expr"
|
||||||
|
type aliasExpr struct {
|
||||||
|
expr Sqlizer
|
||||||
|
alias string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias allows to define alias for column in SelectBuilder. Useful when column is
|
||||||
|
// defined as complex expression like IF or CASE
|
||||||
|
// Ex:
|
||||||
|
// .Column(Alias(caseStmt, "case_column"))
|
||||||
|
func Alias(expr Sqlizer, alias string) aliasExpr {
|
||||||
|
return aliasExpr{expr, alias}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e aliasExpr) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
sql, args, err = e.expr.ToSql()
|
||||||
|
if err == nil {
|
||||||
|
sql = fmt.Sprintf("(%s) AS %s", sql, e.alias)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eq is syntactic sugar for use with Where/Having/Set methods.
|
||||||
|
type Eq map[string]interface{}
|
||||||
|
|
||||||
|
func (eq Eq) toSQL(useNotOpr bool) (sql string, args []interface{}, err error) {
|
||||||
|
if len(eq) == 0 {
|
||||||
|
// Empty Sql{} evaluates to true.
|
||||||
|
sql = sqlTrue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
exprs []string
|
||||||
|
equalOpr = "="
|
||||||
|
inOpr = "IN"
|
||||||
|
nullOpr = "IS"
|
||||||
|
inEmptyExpr = sqlFalse
|
||||||
|
)
|
||||||
|
|
||||||
|
if useNotOpr {
|
||||||
|
equalOpr = "<>"
|
||||||
|
inOpr = "NOT IN"
|
||||||
|
nullOpr = "IS NOT"
|
||||||
|
inEmptyExpr = sqlTrue
|
||||||
|
}
|
||||||
|
|
||||||
|
sortedKeys := getSortedKeys(eq)
|
||||||
|
for _, key := range sortedKeys {
|
||||||
|
var expr string
|
||||||
|
val := eq[key]
|
||||||
|
|
||||||
|
switch v := val.(type) {
|
||||||
|
case driver.Valuer:
|
||||||
|
if val, err = v.Value(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r := reflect.ValueOf(val)
|
||||||
|
if r.Kind() == reflect.Ptr {
|
||||||
|
if r.IsNil() {
|
||||||
|
val = nil
|
||||||
|
} else {
|
||||||
|
val = r.Elem().Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if val == nil {
|
||||||
|
expr = fmt.Sprintf("%s %s NULL", key, nullOpr)
|
||||||
|
} else {
|
||||||
|
if isListType(val) {
|
||||||
|
valVal := reflect.ValueOf(val)
|
||||||
|
if valVal.Len() == 0 {
|
||||||
|
expr = inEmptyExpr
|
||||||
|
if args == nil {
|
||||||
|
args = []interface{}{}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 0; i < valVal.Len(); i++ {
|
||||||
|
args = append(args, valVal.Index(i).Interface())
|
||||||
|
}
|
||||||
|
expr = fmt.Sprintf("%s %s (%s)", key, inOpr, Placeholders(valVal.Len()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expr = fmt.Sprintf("%s %s ?", key, equalOpr)
|
||||||
|
args = append(args, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exprs = append(exprs, expr)
|
||||||
|
}
|
||||||
|
sql = strings.Join(exprs, " AND ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eq Eq) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
return eq.toSQL(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotEq is syntactic sugar for use with Where/Having/Set methods.
|
||||||
|
// Ex:
|
||||||
|
// .Where(NotEq{"id": 1}) == "id <> 1"
|
||||||
|
type NotEq Eq
|
||||||
|
|
||||||
|
func (neq NotEq) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
return Eq(neq).toSQL(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like is syntactic sugar for use with LIKE conditions.
|
||||||
|
// Ex:
|
||||||
|
// .Where(Like{"name": "%irrel"})
|
||||||
|
type Like map[string]interface{}
|
||||||
|
|
||||||
|
func (lk Like) toSql(opr string) (sql string, args []interface{}, err error) {
|
||||||
|
var exprs []string
|
||||||
|
for key, val := range lk {
|
||||||
|
expr := ""
|
||||||
|
|
||||||
|
switch v := val.(type) {
|
||||||
|
case driver.Valuer:
|
||||||
|
if val, err = v.Value(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if val == nil {
|
||||||
|
err = fmt.Errorf("cannot use null with like operators")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if isListType(val) {
|
||||||
|
err = fmt.Errorf("cannot use array or slice with like operators")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
expr = fmt.Sprintf("%s %s ?", key, opr)
|
||||||
|
args = append(args, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exprs = append(exprs, expr)
|
||||||
|
}
|
||||||
|
sql = strings.Join(exprs, " AND ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lk Like) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
return lk.toSql("LIKE")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotLike is syntactic sugar for use with LIKE conditions.
|
||||||
|
// Ex:
|
||||||
|
// .Where(NotLike{"name": "%irrel"})
|
||||||
|
type NotLike Like
|
||||||
|
|
||||||
|
func (nlk NotLike) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
return Like(nlk).toSql("NOT LIKE")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ILike is syntactic sugar for use with ILIKE conditions.
|
||||||
|
// Ex:
|
||||||
|
// .Where(ILike{"name": "sq%"})
|
||||||
|
type ILike Like
|
||||||
|
|
||||||
|
func (ilk ILike) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
return Like(ilk).toSql("ILIKE")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotILike is syntactic sugar for use with ILIKE conditions.
|
||||||
|
// Ex:
|
||||||
|
// .Where(NotILike{"name": "sq%"})
|
||||||
|
type NotILike Like
|
||||||
|
|
||||||
|
func (nilk NotILike) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
return Like(nilk).toSql("NOT ILIKE")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lt is syntactic sugar for use with Where/Having/Set methods.
|
||||||
|
// Ex:
|
||||||
|
// .Where(Lt{"id": 1})
|
||||||
|
type Lt map[string]interface{}
|
||||||
|
|
||||||
|
func (lt Lt) toSql(opposite, orEq bool) (sql string, args []interface{}, err error) {
|
||||||
|
var (
|
||||||
|
exprs []string
|
||||||
|
opr = "<"
|
||||||
|
)
|
||||||
|
|
||||||
|
if opposite {
|
||||||
|
opr = ">"
|
||||||
|
}
|
||||||
|
|
||||||
|
if orEq {
|
||||||
|
opr = fmt.Sprintf("%s%s", opr, "=")
|
||||||
|
}
|
||||||
|
|
||||||
|
sortedKeys := getSortedKeys(lt)
|
||||||
|
for _, key := range sortedKeys {
|
||||||
|
var expr string
|
||||||
|
val := lt[key]
|
||||||
|
|
||||||
|
switch v := val.(type) {
|
||||||
|
case driver.Valuer:
|
||||||
|
if val, err = v.Value(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if val == nil {
|
||||||
|
err = fmt.Errorf("cannot use null with less than or greater than operators")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isListType(val) {
|
||||||
|
err = fmt.Errorf("cannot use array or slice with less than or greater than operators")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expr = fmt.Sprintf("%s %s ?", key, opr)
|
||||||
|
args = append(args, val)
|
||||||
|
|
||||||
|
exprs = append(exprs, expr)
|
||||||
|
}
|
||||||
|
sql = strings.Join(exprs, " AND ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt Lt) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
return lt.toSql(false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LtOrEq is syntactic sugar for use with Where/Having/Set methods.
|
||||||
|
// Ex:
|
||||||
|
// .Where(LtOrEq{"id": 1}) == "id <= 1"
|
||||||
|
type LtOrEq Lt
|
||||||
|
|
||||||
|
func (ltOrEq LtOrEq) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
return Lt(ltOrEq).toSql(false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gt is syntactic sugar for use with Where/Having/Set methods.
|
||||||
|
// Ex:
|
||||||
|
// .Where(Gt{"id": 1}) == "id > 1"
|
||||||
|
type Gt Lt
|
||||||
|
|
||||||
|
func (gt Gt) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
return Lt(gt).toSql(true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GtOrEq is syntactic sugar for use with Where/Having/Set methods.
|
||||||
|
// Ex:
|
||||||
|
// .Where(GtOrEq{"id": 1}) == "id >= 1"
|
||||||
|
type GtOrEq Lt
|
||||||
|
|
||||||
|
func (gtOrEq GtOrEq) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
return Lt(gtOrEq).toSql(true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
type conj []Sqlizer
|
||||||
|
|
||||||
|
func (c conj) join(sep, defaultExpr string) (sql string, args []interface{}, err error) {
|
||||||
|
if len(c) == 0 {
|
||||||
|
return defaultExpr, []interface{}{}, nil
|
||||||
|
}
|
||||||
|
var sqlParts []string
|
||||||
|
for _, sqlizer := range c {
|
||||||
|
partSQL, partArgs, err := nestedToSql(sqlizer)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
if partSQL != "" {
|
||||||
|
sqlParts = append(sqlParts, partSQL)
|
||||||
|
args = append(args, partArgs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(sqlParts) > 0 {
|
||||||
|
sql = fmt.Sprintf("(%s)", strings.Join(sqlParts, sep))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// And conjunction Sqlizers
|
||||||
|
type And conj
|
||||||
|
|
||||||
|
func (a And) ToSql() (string, []interface{}, error) {
|
||||||
|
return conj(a).join(" AND ", sqlTrue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or conjunction Sqlizers
|
||||||
|
type Or conj
|
||||||
|
|
||||||
|
func (o Or) ToSql() (string, []interface{}, error) {
|
||||||
|
return conj(o).join(" OR ", sqlFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSortedKeys(exp map[string]interface{}) []string {
|
||||||
|
sortedKeys := make([]string, 0, len(exp))
|
||||||
|
for k := range exp {
|
||||||
|
sortedKeys = append(sortedKeys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(sortedKeys)
|
||||||
|
return sortedKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func isListType(val interface{}) bool {
|
||||||
|
if driver.IsValue(val) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
valVal := reflect.ValueOf(val)
|
||||||
|
return valVal.Kind() == reflect.Array || valVal.Kind() == reflect.Slice
|
||||||
|
}
|
|
@ -0,0 +1,298 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/lann/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type insertData struct {
|
||||||
|
PlaceholderFormat PlaceholderFormat
|
||||||
|
RunWith BaseRunner
|
||||||
|
Prefixes []Sqlizer
|
||||||
|
StatementKeyword string
|
||||||
|
Options []string
|
||||||
|
Into string
|
||||||
|
Columns []string
|
||||||
|
Values [][]interface{}
|
||||||
|
Suffixes []Sqlizer
|
||||||
|
Select *SelectBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *insertData) Exec() (sql.Result, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
return ExecWith(d.RunWith, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *insertData) Query() (*sql.Rows, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
return QueryWith(d.RunWith, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *insertData) QueryRow() RowScanner {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return &Row{err: RunnerNotSet}
|
||||||
|
}
|
||||||
|
queryRower, ok := d.RunWith.(QueryRower)
|
||||||
|
if !ok {
|
||||||
|
return &Row{err: RunnerNotQueryRunner}
|
||||||
|
}
|
||||||
|
return QueryRowWith(queryRower, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *insertData) ToSql() (sqlStr string, args []interface{}, err error) {
|
||||||
|
if len(d.Into) == 0 {
|
||||||
|
err = errors.New("insert statements must specify a table")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(d.Values) == 0 && d.Select == nil {
|
||||||
|
err = errors.New("insert statements must have at least one set of values or select clause")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sql := &bytes.Buffer{}
|
||||||
|
|
||||||
|
if len(d.Prefixes) > 0 {
|
||||||
|
args, err = appendToSql(d.Prefixes, sql, " ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.StatementKeyword == "" {
|
||||||
|
sql.WriteString("INSERT ")
|
||||||
|
} else {
|
||||||
|
sql.WriteString(d.StatementKeyword)
|
||||||
|
sql.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Options) > 0 {
|
||||||
|
sql.WriteString(strings.Join(d.Options, " "))
|
||||||
|
sql.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.WriteString("INTO ")
|
||||||
|
sql.WriteString(d.Into)
|
||||||
|
sql.WriteString(" ")
|
||||||
|
|
||||||
|
if len(d.Columns) > 0 {
|
||||||
|
sql.WriteString("(")
|
||||||
|
sql.WriteString(strings.Join(d.Columns, ","))
|
||||||
|
sql.WriteString(") ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.Select != nil {
|
||||||
|
args, err = d.appendSelectToSQL(sql, args)
|
||||||
|
} else {
|
||||||
|
args, err = d.appendValuesToSQL(sql, args)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Suffixes) > 0 {
|
||||||
|
sql.WriteString(" ")
|
||||||
|
args, err = appendToSql(d.Suffixes, sql, " ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *insertData) appendValuesToSQL(w io.Writer, args []interface{}) ([]interface{}, error) {
|
||||||
|
if len(d.Values) == 0 {
|
||||||
|
return args, errors.New("values for insert statements are not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
io.WriteString(w, "VALUES ")
|
||||||
|
|
||||||
|
valuesStrings := make([]string, len(d.Values))
|
||||||
|
for r, row := range d.Values {
|
||||||
|
valueStrings := make([]string, len(row))
|
||||||
|
for v, val := range row {
|
||||||
|
if vs, ok := val.(Sqlizer); ok {
|
||||||
|
vsql, vargs, err := vs.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
valueStrings[v] = vsql
|
||||||
|
args = append(args, vargs...)
|
||||||
|
} else {
|
||||||
|
valueStrings[v] = "?"
|
||||||
|
args = append(args, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
valuesStrings[r] = fmt.Sprintf("(%s)", strings.Join(valueStrings, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
io.WriteString(w, strings.Join(valuesStrings, ","))
|
||||||
|
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *insertData) appendSelectToSQL(w io.Writer, args []interface{}) ([]interface{}, error) {
|
||||||
|
if d.Select == nil {
|
||||||
|
return args, errors.New("select clause for insert statements are not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
selectClause, sArgs, err := d.Select.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return args, err
|
||||||
|
}
|
||||||
|
|
||||||
|
io.WriteString(w, selectClause)
|
||||||
|
args = append(args, sArgs...)
|
||||||
|
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builder
|
||||||
|
|
||||||
|
// InsertBuilder builds SQL INSERT statements.
|
||||||
|
type InsertBuilder builder.Builder
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
builder.Register(InsertBuilder{}, insertData{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format methods
|
||||||
|
|
||||||
|
// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the
|
||||||
|
// query.
|
||||||
|
func (b InsertBuilder) PlaceholderFormat(f PlaceholderFormat) InsertBuilder {
|
||||||
|
return builder.Set(b, "PlaceholderFormat", f).(InsertBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runner methods
|
||||||
|
|
||||||
|
// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec.
|
||||||
|
func (b InsertBuilder) RunWith(runner BaseRunner) InsertBuilder {
|
||||||
|
return setRunWith(b, runner).(InsertBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec builds and Execs the query with the Runner set by RunWith.
|
||||||
|
func (b InsertBuilder) Exec() (sql.Result, error) {
|
||||||
|
data := builder.GetStruct(b).(insertData)
|
||||||
|
return data.Exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query builds and Querys the query with the Runner set by RunWith.
|
||||||
|
func (b InsertBuilder) Query() (*sql.Rows, error) {
|
||||||
|
data := builder.GetStruct(b).(insertData)
|
||||||
|
return data.Query()
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRow builds and QueryRows the query with the Runner set by RunWith.
|
||||||
|
func (b InsertBuilder) QueryRow() RowScanner {
|
||||||
|
data := builder.GetStruct(b).(insertData)
|
||||||
|
return data.QueryRow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan is a shortcut for QueryRow().Scan.
|
||||||
|
func (b InsertBuilder) Scan(dest ...interface{}) error {
|
||||||
|
return b.QueryRow().Scan(dest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SQL methods
|
||||||
|
|
||||||
|
// ToSql builds the query into a SQL string and bound args.
|
||||||
|
func (b InsertBuilder) ToSql() (string, []interface{}, error) {
|
||||||
|
data := builder.GetStruct(b).(insertData)
|
||||||
|
return data.ToSql()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustSql builds the query into a SQL string and bound args.
|
||||||
|
// It panics if there are any errors.
|
||||||
|
func (b InsertBuilder) MustSql() (string, []interface{}) {
|
||||||
|
sql, args, err := b.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sql, args
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix adds an expression to the beginning of the query
|
||||||
|
func (b InsertBuilder) Prefix(sql string, args ...interface{}) InsertBuilder {
|
||||||
|
return b.PrefixExpr(Expr(sql, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrefixExpr adds an expression to the very beginning of the query
|
||||||
|
func (b InsertBuilder) PrefixExpr(expr Sqlizer) InsertBuilder {
|
||||||
|
return builder.Append(b, "Prefixes", expr).(InsertBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options adds keyword options before the INTO clause of the query.
|
||||||
|
func (b InsertBuilder) Options(options ...string) InsertBuilder {
|
||||||
|
return builder.Extend(b, "Options", options).(InsertBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Into sets the INTO clause of the query.
|
||||||
|
func (b InsertBuilder) Into(from string) InsertBuilder {
|
||||||
|
return builder.Set(b, "Into", from).(InsertBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Columns adds insert columns to the query.
|
||||||
|
func (b InsertBuilder) Columns(columns ...string) InsertBuilder {
|
||||||
|
return builder.Extend(b, "Columns", columns).(InsertBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values adds a single row's values to the query.
|
||||||
|
func (b InsertBuilder) Values(values ...interface{}) InsertBuilder {
|
||||||
|
return builder.Append(b, "Values", values).(InsertBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suffix adds an expression to the end of the query
|
||||||
|
func (b InsertBuilder) Suffix(sql string, args ...interface{}) InsertBuilder {
|
||||||
|
return b.SuffixExpr(Expr(sql, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuffixExpr adds an expression to the end of the query
|
||||||
|
func (b InsertBuilder) SuffixExpr(expr Sqlizer) InsertBuilder {
|
||||||
|
return builder.Append(b, "Suffixes", expr).(InsertBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMap set columns and values for insert builder from a map of column name and value
|
||||||
|
// note that it will reset all previous columns and values was set if any
|
||||||
|
func (b InsertBuilder) SetMap(clauses map[string]interface{}) InsertBuilder {
|
||||||
|
// Keep the columns in a consistent order by sorting the column key string.
|
||||||
|
cols := make([]string, 0, len(clauses))
|
||||||
|
for col := range clauses {
|
||||||
|
cols = append(cols, col)
|
||||||
|
}
|
||||||
|
sort.Strings(cols)
|
||||||
|
|
||||||
|
vals := make([]interface{}, 0, len(clauses))
|
||||||
|
for _, col := range cols {
|
||||||
|
vals = append(vals, clauses[col])
|
||||||
|
}
|
||||||
|
|
||||||
|
b = builder.Set(b, "Columns", cols).(InsertBuilder)
|
||||||
|
b = builder.Set(b, "Values", [][]interface{}{vals}).(InsertBuilder)
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select set Select clause for insert query
|
||||||
|
// If Values and Select are used, then Select has higher priority
|
||||||
|
func (b InsertBuilder) Select(sb SelectBuilder) InsertBuilder {
|
||||||
|
return builder.Set(b, "Select", &sb).(InsertBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b InsertBuilder) statementKeyword(keyword string) InsertBuilder {
|
||||||
|
return builder.Set(b, "StatementKeyword", keyword).(InsertBuilder)
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/lann/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *insertData) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
ctxRunner, ok := d.RunWith.(ExecerContext)
|
||||||
|
if !ok {
|
||||||
|
return nil, NoContextSupport
|
||||||
|
}
|
||||||
|
return ExecContextWith(ctx, ctxRunner, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *insertData) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
ctxRunner, ok := d.RunWith.(QueryerContext)
|
||||||
|
if !ok {
|
||||||
|
return nil, NoContextSupport
|
||||||
|
}
|
||||||
|
return QueryContextWith(ctx, ctxRunner, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *insertData) QueryRowContext(ctx context.Context) RowScanner {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return &Row{err: RunnerNotSet}
|
||||||
|
}
|
||||||
|
queryRower, ok := d.RunWith.(QueryRowerContext)
|
||||||
|
if !ok {
|
||||||
|
if _, ok := d.RunWith.(QueryerContext); !ok {
|
||||||
|
return &Row{err: RunnerNotQueryRunner}
|
||||||
|
}
|
||||||
|
return &Row{err: NoContextSupport}
|
||||||
|
}
|
||||||
|
return QueryRowContextWith(ctx, queryRower, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecContext builds and ExecContexts the query with the Runner set by RunWith.
|
||||||
|
func (b InsertBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||||
|
data := builder.GetStruct(b).(insertData)
|
||||||
|
return data.ExecContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryContext builds and QueryContexts the query with the Runner set by RunWith.
|
||||||
|
func (b InsertBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||||
|
data := builder.GetStruct(b).(insertData)
|
||||||
|
return data.QueryContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith.
|
||||||
|
func (b InsertBuilder) QueryRowContext(ctx context.Context) RowScanner {
|
||||||
|
data := builder.GetStruct(b).(insertData)
|
||||||
|
return data.QueryRowContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScanContext is a shortcut for QueryRowContext().Scan.
|
||||||
|
func (b InsertBuilder) ScanContext(ctx context.Context, dest ...interface{}) error {
|
||||||
|
return b.QueryRowContext(ctx).Scan(dest...)
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type part struct {
|
||||||
|
pred interface{}
|
||||||
|
args []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPart(pred interface{}, args ...interface{}) Sqlizer {
|
||||||
|
return &part{pred, args}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p part) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
switch pred := p.pred.(type) {
|
||||||
|
case nil:
|
||||||
|
// no-op
|
||||||
|
case Sqlizer:
|
||||||
|
sql, args, err = nestedToSql(pred)
|
||||||
|
case string:
|
||||||
|
sql = pred
|
||||||
|
args = p.args
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("expected string or Sqlizer, not %T", pred)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func nestedToSql(s Sqlizer) (string, []interface{}, error) {
|
||||||
|
if raw, ok := s.(rawSqlizer); ok {
|
||||||
|
return raw.toSqlRaw()
|
||||||
|
} else {
|
||||||
|
return s.ToSql()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendToSql(parts []Sqlizer, w io.Writer, sep string, args []interface{}) ([]interface{}, error) {
|
||||||
|
for i, p := range parts {
|
||||||
|
partSql, partArgs, err := nestedToSql(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(partSql) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > 0 {
|
||||||
|
_, err := io.WriteString(w, sep)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.WriteString(w, partSql)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
args = append(args, partArgs...)
|
||||||
|
}
|
||||||
|
return args, nil
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PlaceholderFormat is the interface that wraps the ReplacePlaceholders method.
|
||||||
|
//
|
||||||
|
// ReplacePlaceholders takes a SQL statement and replaces each question mark
|
||||||
|
// placeholder with a (possibly different) SQL placeholder.
|
||||||
|
type PlaceholderFormat interface {
|
||||||
|
ReplacePlaceholders(sql string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type placeholderDebugger interface {
|
||||||
|
debugPlaceholder() string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Question is a PlaceholderFormat instance that leaves placeholders as
|
||||||
|
// question marks.
|
||||||
|
Question = questionFormat{}
|
||||||
|
|
||||||
|
// Dollar is a PlaceholderFormat instance that replaces placeholders with
|
||||||
|
// dollar-prefixed positional placeholders (e.g. $1, $2, $3).
|
||||||
|
Dollar = dollarFormat{}
|
||||||
|
|
||||||
|
// Colon is a PlaceholderFormat instance that replaces placeholders with
|
||||||
|
// colon-prefixed positional placeholders (e.g. :1, :2, :3).
|
||||||
|
Colon = colonFormat{}
|
||||||
|
|
||||||
|
// AtP is a PlaceholderFormat instance that replaces placeholders with
|
||||||
|
// "@p"-prefixed positional placeholders (e.g. @p1, @p2, @p3).
|
||||||
|
AtP = atpFormat{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type questionFormat struct{}
|
||||||
|
|
||||||
|
func (questionFormat) ReplacePlaceholders(sql string) (string, error) {
|
||||||
|
return sql, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (questionFormat) debugPlaceholder() string {
|
||||||
|
return "?"
|
||||||
|
}
|
||||||
|
|
||||||
|
type dollarFormat struct{}
|
||||||
|
|
||||||
|
func (dollarFormat) ReplacePlaceholders(sql string) (string, error) {
|
||||||
|
return replacePositionalPlaceholders(sql, "$")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dollarFormat) debugPlaceholder() string {
|
||||||
|
return "$"
|
||||||
|
}
|
||||||
|
|
||||||
|
type colonFormat struct{}
|
||||||
|
|
||||||
|
func (colonFormat) ReplacePlaceholders(sql string) (string, error) {
|
||||||
|
return replacePositionalPlaceholders(sql, ":")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (colonFormat) debugPlaceholder() string {
|
||||||
|
return ":"
|
||||||
|
}
|
||||||
|
|
||||||
|
type atpFormat struct{}
|
||||||
|
|
||||||
|
func (atpFormat) ReplacePlaceholders(sql string) (string, error) {
|
||||||
|
return replacePositionalPlaceholders(sql, "@p")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (atpFormat) debugPlaceholder() string {
|
||||||
|
return "@p"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placeholders returns a string with count ? placeholders joined with commas.
|
||||||
|
func Placeholders(count int) string {
|
||||||
|
if count < 1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Repeat(",?", count)[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func replacePositionalPlaceholders(sql, prefix string) (string, error) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
i := 0
|
||||||
|
for {
|
||||||
|
p := strings.Index(sql, "?")
|
||||||
|
if p == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sql[p:]) > 1 && sql[p:p+2] == "??" { // escape ?? => ?
|
||||||
|
buf.WriteString(sql[:p])
|
||||||
|
buf.WriteString("?")
|
||||||
|
if len(sql[p:]) == 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sql = sql[p+2:]
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
buf.WriteString(sql[:p])
|
||||||
|
fmt.Fprintf(buf, "%s%d", prefix, i)
|
||||||
|
sql = sql[p+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(sql)
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
// RowScanner is the interface that wraps the Scan method.
|
||||||
|
//
|
||||||
|
// Scan behaves like database/sql.Row.Scan.
|
||||||
|
type RowScanner interface {
|
||||||
|
Scan(...interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row wraps database/sql.Row to let squirrel return new errors on Scan.
|
||||||
|
type Row struct {
|
||||||
|
RowScanner
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan returns Row.err or calls RowScanner.Scan.
|
||||||
|
func (r *Row) Scan(dest ...interface{}) error {
|
||||||
|
if r.err != nil {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
return r.RowScanner.Scan(dest...)
|
||||||
|
}
|
|
@ -0,0 +1,403 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/lann/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type selectData struct {
|
||||||
|
PlaceholderFormat PlaceholderFormat
|
||||||
|
RunWith BaseRunner
|
||||||
|
Prefixes []Sqlizer
|
||||||
|
Options []string
|
||||||
|
Columns []Sqlizer
|
||||||
|
From Sqlizer
|
||||||
|
Joins []Sqlizer
|
||||||
|
WhereParts []Sqlizer
|
||||||
|
GroupBys []string
|
||||||
|
HavingParts []Sqlizer
|
||||||
|
OrderByParts []Sqlizer
|
||||||
|
Limit string
|
||||||
|
Offset string
|
||||||
|
Suffixes []Sqlizer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *selectData) Exec() (sql.Result, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
return ExecWith(d.RunWith, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *selectData) Query() (*sql.Rows, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
return QueryWith(d.RunWith, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *selectData) QueryRow() RowScanner {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return &Row{err: RunnerNotSet}
|
||||||
|
}
|
||||||
|
queryRower, ok := d.RunWith.(QueryRower)
|
||||||
|
if !ok {
|
||||||
|
return &Row{err: RunnerNotQueryRunner}
|
||||||
|
}
|
||||||
|
return QueryRowWith(queryRower, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *selectData) ToSql() (sqlStr string, args []interface{}, err error) {
|
||||||
|
sqlStr, args, err = d.toSqlRaw()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sqlStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *selectData) toSqlRaw() (sqlStr string, args []interface{}, err error) {
|
||||||
|
if len(d.Columns) == 0 {
|
||||||
|
err = fmt.Errorf("select statements must have at least one result column")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sql := &bytes.Buffer{}
|
||||||
|
|
||||||
|
if len(d.Prefixes) > 0 {
|
||||||
|
args, err = appendToSql(d.Prefixes, sql, " ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.WriteString("SELECT ")
|
||||||
|
|
||||||
|
if len(d.Options) > 0 {
|
||||||
|
sql.WriteString(strings.Join(d.Options, " "))
|
||||||
|
sql.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Columns) > 0 {
|
||||||
|
args, err = appendToSql(d.Columns, sql, ", ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.From != nil {
|
||||||
|
sql.WriteString(" FROM ")
|
||||||
|
args, err = appendToSql([]Sqlizer{d.From}, sql, "", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Joins) > 0 {
|
||||||
|
sql.WriteString(" ")
|
||||||
|
args, err = appendToSql(d.Joins, sql, " ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.WhereParts) > 0 {
|
||||||
|
sql.WriteString(" WHERE ")
|
||||||
|
args, err = appendToSql(d.WhereParts, sql, " AND ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.GroupBys) > 0 {
|
||||||
|
sql.WriteString(" GROUP BY ")
|
||||||
|
sql.WriteString(strings.Join(d.GroupBys, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.HavingParts) > 0 {
|
||||||
|
sql.WriteString(" HAVING ")
|
||||||
|
args, err = appendToSql(d.HavingParts, sql, " AND ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.OrderByParts) > 0 {
|
||||||
|
sql.WriteString(" ORDER BY ")
|
||||||
|
args, err = appendToSql(d.OrderByParts, sql, ", ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Limit) > 0 {
|
||||||
|
sql.WriteString(" LIMIT ")
|
||||||
|
sql.WriteString(d.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Offset) > 0 {
|
||||||
|
sql.WriteString(" OFFSET ")
|
||||||
|
sql.WriteString(d.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Suffixes) > 0 {
|
||||||
|
sql.WriteString(" ")
|
||||||
|
|
||||||
|
args, err = appendToSql(d.Suffixes, sql, " ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlStr = sql.String()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builder
|
||||||
|
|
||||||
|
// SelectBuilder builds SQL SELECT statements.
|
||||||
|
type SelectBuilder builder.Builder
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
builder.Register(SelectBuilder{}, selectData{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format methods
|
||||||
|
|
||||||
|
// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the
|
||||||
|
// query.
|
||||||
|
func (b SelectBuilder) PlaceholderFormat(f PlaceholderFormat) SelectBuilder {
|
||||||
|
return builder.Set(b, "PlaceholderFormat", f).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runner methods
|
||||||
|
|
||||||
|
// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec.
|
||||||
|
// For most cases runner will be a database connection.
|
||||||
|
//
|
||||||
|
// Internally we use this to mock out the database connection for testing.
|
||||||
|
func (b SelectBuilder) RunWith(runner BaseRunner) SelectBuilder {
|
||||||
|
return setRunWith(b, runner).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec builds and Execs the query with the Runner set by RunWith.
|
||||||
|
func (b SelectBuilder) Exec() (sql.Result, error) {
|
||||||
|
data := builder.GetStruct(b).(selectData)
|
||||||
|
return data.Exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query builds and Querys the query with the Runner set by RunWith.
|
||||||
|
func (b SelectBuilder) Query() (*sql.Rows, error) {
|
||||||
|
data := builder.GetStruct(b).(selectData)
|
||||||
|
return data.Query()
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRow builds and QueryRows the query with the Runner set by RunWith.
|
||||||
|
func (b SelectBuilder) QueryRow() RowScanner {
|
||||||
|
data := builder.GetStruct(b).(selectData)
|
||||||
|
return data.QueryRow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan is a shortcut for QueryRow().Scan.
|
||||||
|
func (b SelectBuilder) Scan(dest ...interface{}) error {
|
||||||
|
return b.QueryRow().Scan(dest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SQL methods
|
||||||
|
|
||||||
|
// ToSql builds the query into a SQL string and bound args.
|
||||||
|
func (b SelectBuilder) ToSql() (string, []interface{}, error) {
|
||||||
|
data := builder.GetStruct(b).(selectData)
|
||||||
|
return data.ToSql()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b SelectBuilder) toSqlRaw() (string, []interface{}, error) {
|
||||||
|
data := builder.GetStruct(b).(selectData)
|
||||||
|
return data.toSqlRaw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustSql builds the query into a SQL string and bound args.
|
||||||
|
// It panics if there are any errors.
|
||||||
|
func (b SelectBuilder) MustSql() (string, []interface{}) {
|
||||||
|
sql, args, err := b.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sql, args
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix adds an expression to the beginning of the query
|
||||||
|
func (b SelectBuilder) Prefix(sql string, args ...interface{}) SelectBuilder {
|
||||||
|
return b.PrefixExpr(Expr(sql, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrefixExpr adds an expression to the very beginning of the query
|
||||||
|
func (b SelectBuilder) PrefixExpr(expr Sqlizer) SelectBuilder {
|
||||||
|
return builder.Append(b, "Prefixes", expr).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distinct adds a DISTINCT clause to the query.
|
||||||
|
func (b SelectBuilder) Distinct() SelectBuilder {
|
||||||
|
return b.Options("DISTINCT")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options adds select option to the query
|
||||||
|
func (b SelectBuilder) Options(options ...string) SelectBuilder {
|
||||||
|
return builder.Extend(b, "Options", options).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Columns adds result columns to the query.
|
||||||
|
func (b SelectBuilder) Columns(columns ...string) SelectBuilder {
|
||||||
|
parts := make([]interface{}, 0, len(columns))
|
||||||
|
for _, str := range columns {
|
||||||
|
parts = append(parts, newPart(str))
|
||||||
|
}
|
||||||
|
return builder.Extend(b, "Columns", parts).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveColumns remove all columns from query.
|
||||||
|
// Must add a new column with Column or Columns methods, otherwise
|
||||||
|
// return a error.
|
||||||
|
func (b SelectBuilder) RemoveColumns() SelectBuilder {
|
||||||
|
return builder.Delete(b, "Columns").(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Column adds a result column to the query.
|
||||||
|
// Unlike Columns, Column accepts args which will be bound to placeholders in
|
||||||
|
// the columns string, for example:
|
||||||
|
// Column("IF(col IN ("+squirrel.Placeholders(3)+"), 1, 0) as col", 1, 2, 3)
|
||||||
|
func (b SelectBuilder) Column(column interface{}, args ...interface{}) SelectBuilder {
|
||||||
|
return builder.Append(b, "Columns", newPart(column, args...)).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// From sets the FROM clause of the query.
|
||||||
|
func (b SelectBuilder) From(from string) SelectBuilder {
|
||||||
|
return builder.Set(b, "From", newPart(from)).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromSelect sets a subquery into the FROM clause of the query.
|
||||||
|
func (b SelectBuilder) FromSelect(from SelectBuilder, alias string) SelectBuilder {
|
||||||
|
// Prevent misnumbered parameters in nested selects (#183).
|
||||||
|
from = from.PlaceholderFormat(Question)
|
||||||
|
return builder.Set(b, "From", Alias(from, alias)).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinClause adds a join clause to the query.
|
||||||
|
func (b SelectBuilder) JoinClause(pred interface{}, args ...interface{}) SelectBuilder {
|
||||||
|
return builder.Append(b, "Joins", newPart(pred, args...)).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join adds a JOIN clause to the query.
|
||||||
|
func (b SelectBuilder) Join(join string, rest ...interface{}) SelectBuilder {
|
||||||
|
return b.JoinClause("JOIN "+join, rest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftJoin adds a LEFT JOIN clause to the query.
|
||||||
|
func (b SelectBuilder) LeftJoin(join string, rest ...interface{}) SelectBuilder {
|
||||||
|
return b.JoinClause("LEFT JOIN "+join, rest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RightJoin adds a RIGHT JOIN clause to the query.
|
||||||
|
func (b SelectBuilder) RightJoin(join string, rest ...interface{}) SelectBuilder {
|
||||||
|
return b.JoinClause("RIGHT JOIN "+join, rest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InnerJoin adds a INNER JOIN clause to the query.
|
||||||
|
func (b SelectBuilder) InnerJoin(join string, rest ...interface{}) SelectBuilder {
|
||||||
|
return b.JoinClause("INNER JOIN "+join, rest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CrossJoin adds a CROSS JOIN clause to the query.
|
||||||
|
func (b SelectBuilder) CrossJoin(join string, rest ...interface{}) SelectBuilder {
|
||||||
|
return b.JoinClause("CROSS JOIN "+join, rest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where adds an expression to the WHERE clause of the query.
|
||||||
|
//
|
||||||
|
// Expressions are ANDed together in the generated SQL.
|
||||||
|
//
|
||||||
|
// Where accepts several types for its pred argument:
|
||||||
|
//
|
||||||
|
// nil OR "" - ignored.
|
||||||
|
//
|
||||||
|
// string - SQL expression.
|
||||||
|
// If the expression has SQL placeholders then a set of arguments must be passed
|
||||||
|
// as well, one for each placeholder.
|
||||||
|
//
|
||||||
|
// map[string]interface{} OR Eq - map of SQL expressions to values. Each key is
|
||||||
|
// transformed into an expression like "<key> = ?", with the corresponding value
|
||||||
|
// bound to the placeholder. If the value is nil, the expression will be "<key>
|
||||||
|
// IS NULL". If the value is an array or slice, the expression will be "<key> IN
|
||||||
|
// (?,?,...)", with one placeholder for each item in the value. These expressions
|
||||||
|
// are ANDed together.
|
||||||
|
//
|
||||||
|
// Where will panic if pred isn't any of the above types.
|
||||||
|
func (b SelectBuilder) Where(pred interface{}, args ...interface{}) SelectBuilder {
|
||||||
|
if pred == nil || pred == "" {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupBy adds GROUP BY expressions to the query.
|
||||||
|
func (b SelectBuilder) GroupBy(groupBys ...string) SelectBuilder {
|
||||||
|
return builder.Extend(b, "GroupBys", groupBys).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Having adds an expression to the HAVING clause of the query.
|
||||||
|
//
|
||||||
|
// See Where.
|
||||||
|
func (b SelectBuilder) Having(pred interface{}, rest ...interface{}) SelectBuilder {
|
||||||
|
return builder.Append(b, "HavingParts", newWherePart(pred, rest...)).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderByClause adds ORDER BY clause to the query.
|
||||||
|
func (b SelectBuilder) OrderByClause(pred interface{}, args ...interface{}) SelectBuilder {
|
||||||
|
return builder.Append(b, "OrderByParts", newPart(pred, args...)).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderBy adds ORDER BY expressions to the query.
|
||||||
|
func (b SelectBuilder) OrderBy(orderBys ...string) SelectBuilder {
|
||||||
|
for _, orderBy := range orderBys {
|
||||||
|
b = b.OrderByClause(orderBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit sets a LIMIT clause on the query.
|
||||||
|
func (b SelectBuilder) Limit(limit uint64) SelectBuilder {
|
||||||
|
return builder.Set(b, "Limit", fmt.Sprintf("%d", limit)).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit ALL allows to access all records with limit
|
||||||
|
func (b SelectBuilder) RemoveLimit() SelectBuilder {
|
||||||
|
return builder.Delete(b, "Limit").(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset sets a OFFSET clause on the query.
|
||||||
|
func (b SelectBuilder) Offset(offset uint64) SelectBuilder {
|
||||||
|
return builder.Set(b, "Offset", fmt.Sprintf("%d", offset)).(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveOffset removes OFFSET clause.
|
||||||
|
func (b SelectBuilder) RemoveOffset() SelectBuilder {
|
||||||
|
return builder.Delete(b, "Offset").(SelectBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suffix adds an expression to the end of the query
|
||||||
|
func (b SelectBuilder) Suffix(sql string, args ...interface{}) SelectBuilder {
|
||||||
|
return b.SuffixExpr(Expr(sql, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuffixExpr adds an expression to the end of the query
|
||||||
|
func (b SelectBuilder) SuffixExpr(expr Sqlizer) SelectBuilder {
|
||||||
|
return builder.Append(b, "Suffixes", expr).(SelectBuilder)
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/lann/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *selectData) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
ctxRunner, ok := d.RunWith.(ExecerContext)
|
||||||
|
if !ok {
|
||||||
|
return nil, NoContextSupport
|
||||||
|
}
|
||||||
|
return ExecContextWith(ctx, ctxRunner, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *selectData) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
ctxRunner, ok := d.RunWith.(QueryerContext)
|
||||||
|
if !ok {
|
||||||
|
return nil, NoContextSupport
|
||||||
|
}
|
||||||
|
return QueryContextWith(ctx, ctxRunner, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *selectData) QueryRowContext(ctx context.Context) RowScanner {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return &Row{err: RunnerNotSet}
|
||||||
|
}
|
||||||
|
queryRower, ok := d.RunWith.(QueryRowerContext)
|
||||||
|
if !ok {
|
||||||
|
if _, ok := d.RunWith.(QueryerContext); !ok {
|
||||||
|
return &Row{err: RunnerNotQueryRunner}
|
||||||
|
}
|
||||||
|
return &Row{err: NoContextSupport}
|
||||||
|
}
|
||||||
|
return QueryRowContextWith(ctx, queryRower, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecContext builds and ExecContexts the query with the Runner set by RunWith.
|
||||||
|
func (b SelectBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||||
|
data := builder.GetStruct(b).(selectData)
|
||||||
|
return data.ExecContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryContext builds and QueryContexts the query with the Runner set by RunWith.
|
||||||
|
func (b SelectBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||||
|
data := builder.GetStruct(b).(selectData)
|
||||||
|
return data.QueryContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith.
|
||||||
|
func (b SelectBuilder) QueryRowContext(ctx context.Context) RowScanner {
|
||||||
|
data := builder.GetStruct(b).(selectData)
|
||||||
|
return data.QueryRowContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScanContext is a shortcut for QueryRowContext().Scan.
|
||||||
|
func (b SelectBuilder) ScanContext(ctx context.Context, dest ...interface{}) error {
|
||||||
|
return b.QueryRowContext(ctx).Scan(dest...)
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
// Package squirrel provides a fluent SQL generator.
|
||||||
|
//
|
||||||
|
// See https://github.com/Masterminds/squirrel for examples.
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/lann/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sqlizer is the interface that wraps the ToSql method.
|
||||||
|
//
|
||||||
|
// ToSql returns a SQL representation of the Sqlizer, along with a slice of args
|
||||||
|
// as passed to e.g. database/sql.Exec. It can also return an error.
|
||||||
|
type Sqlizer interface {
|
||||||
|
ToSql() (string, []interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawSqlizer is expected to do what Sqlizer does, but without finalizing placeholders.
|
||||||
|
// This is useful for nested queries.
|
||||||
|
type rawSqlizer interface {
|
||||||
|
toSqlRaw() (string, []interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execer is the interface that wraps the Exec method.
|
||||||
|
//
|
||||||
|
// Exec executes the given query as implemented by database/sql.Exec.
|
||||||
|
type Execer interface {
|
||||||
|
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queryer is the interface that wraps the Query method.
|
||||||
|
//
|
||||||
|
// Query executes the given query as implemented by database/sql.Query.
|
||||||
|
type Queryer interface {
|
||||||
|
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRower is the interface that wraps the QueryRow method.
|
||||||
|
//
|
||||||
|
// QueryRow executes the given query as implemented by database/sql.QueryRow.
|
||||||
|
type QueryRower interface {
|
||||||
|
QueryRow(query string, args ...interface{}) RowScanner
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseRunner groups the Execer and Queryer interfaces.
|
||||||
|
type BaseRunner interface {
|
||||||
|
Execer
|
||||||
|
Queryer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runner groups the Execer, Queryer, and QueryRower interfaces.
|
||||||
|
type Runner interface {
|
||||||
|
Execer
|
||||||
|
Queryer
|
||||||
|
QueryRower
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapStdSql wraps a type implementing the standard SQL interface with methods that
|
||||||
|
// squirrel expects.
|
||||||
|
func WrapStdSql(stdSql StdSql) Runner {
|
||||||
|
return &stdsqlRunner{stdSql}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdSql encompasses the standard methods of the *sql.DB type, and other types that
|
||||||
|
// wrap these methods.
|
||||||
|
type StdSql interface {
|
||||||
|
Query(string, ...interface{}) (*sql.Rows, error)
|
||||||
|
QueryRow(string, ...interface{}) *sql.Row
|
||||||
|
Exec(string, ...interface{}) (sql.Result, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type stdsqlRunner struct {
|
||||||
|
StdSql
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *stdsqlRunner) QueryRow(query string, args ...interface{}) RowScanner {
|
||||||
|
return r.StdSql.QueryRow(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setRunWith(b interface{}, runner BaseRunner) interface{} {
|
||||||
|
switch r := runner.(type) {
|
||||||
|
case StdSqlCtx:
|
||||||
|
runner = WrapStdSqlCtx(r)
|
||||||
|
case StdSql:
|
||||||
|
runner = WrapStdSql(r)
|
||||||
|
}
|
||||||
|
return builder.Set(b, "RunWith", runner)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunnerNotSet is returned by methods that need a Runner if it isn't set.
|
||||||
|
var RunnerNotSet = fmt.Errorf("cannot run; no Runner set (RunWith)")
|
||||||
|
|
||||||
|
// RunnerNotQueryRunner is returned by QueryRow if the RunWith value doesn't implement QueryRower.
|
||||||
|
var RunnerNotQueryRunner = fmt.Errorf("cannot QueryRow; Runner is not a QueryRower")
|
||||||
|
|
||||||
|
// ExecWith Execs the SQL returned by s with db.
|
||||||
|
func ExecWith(db Execer, s Sqlizer) (res sql.Result, err error) {
|
||||||
|
query, args, err := s.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return db.Exec(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryWith Querys the SQL returned by s with db.
|
||||||
|
func QueryWith(db Queryer, s Sqlizer) (rows *sql.Rows, err error) {
|
||||||
|
query, args, err := s.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return db.Query(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowWith QueryRows the SQL returned by s with db.
|
||||||
|
func QueryRowWith(db QueryRower, s Sqlizer) RowScanner {
|
||||||
|
query, args, err := s.ToSql()
|
||||||
|
return &Row{RowScanner: db.QueryRow(query, args...), err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugSqlizer calls ToSql on s and shows the approximate SQL to be executed
|
||||||
|
//
|
||||||
|
// If ToSql returns an error, the result of this method will look like:
|
||||||
|
// "[ToSql error: %s]" or "[DebugSqlizer error: %s]"
|
||||||
|
//
|
||||||
|
// IMPORTANT: As its name suggests, this function should only be used for
|
||||||
|
// debugging. While the string result *might* be valid SQL, this function does
|
||||||
|
// not try very hard to ensure it. Additionally, executing the output of this
|
||||||
|
// function with any untrusted user input is certainly insecure.
|
||||||
|
func DebugSqlizer(s Sqlizer) string {
|
||||||
|
sql, args, err := s.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("[ToSql error: %s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var placeholder string
|
||||||
|
downCast, ok := s.(placeholderDebugger)
|
||||||
|
if !ok {
|
||||||
|
placeholder = "?"
|
||||||
|
} else {
|
||||||
|
placeholder = downCast.debugPlaceholder()
|
||||||
|
}
|
||||||
|
// TODO: dedupe this with placeholder.go
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
i := 0
|
||||||
|
for {
|
||||||
|
p := strings.Index(sql, placeholder)
|
||||||
|
if p == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(sql[p:]) > 1 && sql[p:p+2] == "??" { // escape ?? => ?
|
||||||
|
buf.WriteString(sql[:p])
|
||||||
|
buf.WriteString("?")
|
||||||
|
if len(sql[p:]) == 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sql = sql[p+2:]
|
||||||
|
} else {
|
||||||
|
if i+1 > len(args) {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"[DebugSqlizer error: too many placeholders in %#v for %d args]",
|
||||||
|
sql, len(args))
|
||||||
|
}
|
||||||
|
buf.WriteString(sql[:p])
|
||||||
|
fmt.Fprintf(buf, "'%v'", args[i])
|
||||||
|
// advance our sql string "cursor" beyond the arg we placed
|
||||||
|
sql = sql[p+1:]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i < len(args) {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"[DebugSqlizer error: not enough placeholders in %#v for %d args]",
|
||||||
|
sql, len(args))
|
||||||
|
}
|
||||||
|
// "append" any remaning sql that won't need interpolating
|
||||||
|
buf.WriteString(sql)
|
||||||
|
return buf.String()
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NoContextSupport is returned if a db doesn't support Context.
|
||||||
|
var NoContextSupport = errors.New("DB does not support Context")
|
||||||
|
|
||||||
|
// ExecerContext is the interface that wraps the ExecContext method.
|
||||||
|
//
|
||||||
|
// Exec executes the given query as implemented by database/sql.ExecContext.
|
||||||
|
type ExecerContext interface {
|
||||||
|
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryerContext is the interface that wraps the QueryContext method.
|
||||||
|
//
|
||||||
|
// QueryContext executes the given query as implemented by database/sql.QueryContext.
|
||||||
|
type QueryerContext interface {
|
||||||
|
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowerContext is the interface that wraps the QueryRowContext method.
|
||||||
|
//
|
||||||
|
// QueryRowContext executes the given query as implemented by database/sql.QueryRowContext.
|
||||||
|
type QueryRowerContext interface {
|
||||||
|
QueryRowContext(ctx context.Context, query string, args ...interface{}) RowScanner
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunnerContext groups the Runner interface, along with the Context versions of each of
|
||||||
|
// its methods
|
||||||
|
type RunnerContext interface {
|
||||||
|
Runner
|
||||||
|
QueryerContext
|
||||||
|
QueryRowerContext
|
||||||
|
ExecerContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapStdSqlCtx wraps a type implementing the standard SQL interface plus the context
|
||||||
|
// versions of the methods with methods that squirrel expects.
|
||||||
|
func WrapStdSqlCtx(stdSqlCtx StdSqlCtx) RunnerContext {
|
||||||
|
return &stdsqlCtxRunner{stdSqlCtx}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdSqlCtx encompasses the standard methods of the *sql.DB type, along with the Context
|
||||||
|
// versions of those methods, and other types that wrap these methods.
|
||||||
|
type StdSqlCtx interface {
|
||||||
|
StdSql
|
||||||
|
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||||
|
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
|
||||||
|
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type stdsqlCtxRunner struct {
|
||||||
|
StdSqlCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *stdsqlCtxRunner) QueryRow(query string, args ...interface{}) RowScanner {
|
||||||
|
return r.StdSqlCtx.QueryRow(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *stdsqlCtxRunner) QueryRowContext(ctx context.Context, query string, args ...interface{}) RowScanner {
|
||||||
|
return r.StdSqlCtx.QueryRowContext(ctx, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecContextWith ExecContexts the SQL returned by s with db.
|
||||||
|
func ExecContextWith(ctx context.Context, db ExecerContext, s Sqlizer) (res sql.Result, err error) {
|
||||||
|
query, args, err := s.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return db.ExecContext(ctx, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryContextWith QueryContexts the SQL returned by s with db.
|
||||||
|
func QueryContextWith(ctx context.Context, db QueryerContext, s Sqlizer) (rows *sql.Rows, err error) {
|
||||||
|
query, args, err := s.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return db.QueryContext(ctx, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowContextWith QueryRowContexts the SQL returned by s with db.
|
||||||
|
func QueryRowContextWith(ctx context.Context, db QueryRowerContext, s Sqlizer) RowScanner {
|
||||||
|
query, args, err := s.ToSql()
|
||||||
|
return &Row{RowScanner: db.QueryRowContext(ctx, query, args...), err: err}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import "github.com/lann/builder"
|
||||||
|
|
||||||
|
// StatementBuilderType is the type of StatementBuilder.
|
||||||
|
type StatementBuilderType builder.Builder
|
||||||
|
|
||||||
|
// Select returns a SelectBuilder for this StatementBuilderType.
|
||||||
|
func (b StatementBuilderType) Select(columns ...string) SelectBuilder {
|
||||||
|
return SelectBuilder(b).Columns(columns...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert returns a InsertBuilder for this StatementBuilderType.
|
||||||
|
func (b StatementBuilderType) Insert(into string) InsertBuilder {
|
||||||
|
return InsertBuilder(b).Into(into)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace returns a InsertBuilder for this StatementBuilderType with the
|
||||||
|
// statement keyword set to "REPLACE".
|
||||||
|
func (b StatementBuilderType) Replace(into string) InsertBuilder {
|
||||||
|
return InsertBuilder(b).statementKeyword("REPLACE").Into(into)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update returns a UpdateBuilder for this StatementBuilderType.
|
||||||
|
func (b StatementBuilderType) Update(table string) UpdateBuilder {
|
||||||
|
return UpdateBuilder(b).Table(table)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete returns a DeleteBuilder for this StatementBuilderType.
|
||||||
|
func (b StatementBuilderType) Delete(from string) DeleteBuilder {
|
||||||
|
return DeleteBuilder(b).From(from)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlaceholderFormat sets the PlaceholderFormat field for any child builders.
|
||||||
|
func (b StatementBuilderType) PlaceholderFormat(f PlaceholderFormat) StatementBuilderType {
|
||||||
|
return builder.Set(b, "PlaceholderFormat", f).(StatementBuilderType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunWith sets the RunWith field for any child builders.
|
||||||
|
func (b StatementBuilderType) RunWith(runner BaseRunner) StatementBuilderType {
|
||||||
|
return setRunWith(b, runner).(StatementBuilderType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where adds WHERE expressions to the query.
|
||||||
|
//
|
||||||
|
// See SelectBuilder.Where for more information.
|
||||||
|
func (b StatementBuilderType) Where(pred interface{}, args ...interface{}) StatementBuilderType {
|
||||||
|
return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(StatementBuilderType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatementBuilder is a parent builder for other builders, e.g. SelectBuilder.
|
||||||
|
var StatementBuilder = StatementBuilderType(builder.EmptyBuilder).PlaceholderFormat(Question)
|
||||||
|
|
||||||
|
// Select returns a new SelectBuilder, optionally setting some result columns.
|
||||||
|
//
|
||||||
|
// See SelectBuilder.Columns.
|
||||||
|
func Select(columns ...string) SelectBuilder {
|
||||||
|
return StatementBuilder.Select(columns...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert returns a new InsertBuilder with the given table name.
|
||||||
|
//
|
||||||
|
// See InsertBuilder.Into.
|
||||||
|
func Insert(into string) InsertBuilder {
|
||||||
|
return StatementBuilder.Insert(into)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace returns a new InsertBuilder with the statement keyword set to
|
||||||
|
// "REPLACE" and with the given table name.
|
||||||
|
//
|
||||||
|
// See InsertBuilder.Into.
|
||||||
|
func Replace(into string) InsertBuilder {
|
||||||
|
return StatementBuilder.Replace(into)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update returns a new UpdateBuilder with the given table name.
|
||||||
|
//
|
||||||
|
// See UpdateBuilder.Table.
|
||||||
|
func Update(table string) UpdateBuilder {
|
||||||
|
return StatementBuilder.Update(table)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete returns a new DeleteBuilder with the given table name.
|
||||||
|
//
|
||||||
|
// See DeleteBuilder.Table.
|
||||||
|
func Delete(from string) DeleteBuilder {
|
||||||
|
return StatementBuilder.Delete(from)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case returns a new CaseBuilder
|
||||||
|
// "what" represents case value
|
||||||
|
func Case(what ...interface{}) CaseBuilder {
|
||||||
|
b := CaseBuilder(builder.EmptyBuilder)
|
||||||
|
|
||||||
|
switch len(what) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
b = b.what(what[0])
|
||||||
|
default:
|
||||||
|
b = b.what(newPart(what[0], what[1:]...))
|
||||||
|
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Prepareer is the interface that wraps the Prepare method.
|
||||||
|
//
|
||||||
|
// Prepare executes the given query as implemented by database/sql.Prepare.
|
||||||
|
type Preparer interface {
|
||||||
|
Prepare(query string) (*sql.Stmt, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DBProxy groups the Execer, Queryer, QueryRower, and Preparer interfaces.
|
||||||
|
type DBProxy interface {
|
||||||
|
Execer
|
||||||
|
Queryer
|
||||||
|
QueryRower
|
||||||
|
Preparer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: NewStmtCache is defined in stmtcacher_ctx.go (Go >= 1.8) or stmtcacher_noctx.go (Go < 1.8).
|
||||||
|
|
||||||
|
// StmtCache wraps and delegates down to a Preparer type
|
||||||
|
//
|
||||||
|
// It also automatically prepares all statements sent to the underlying Preparer calls
|
||||||
|
// for Exec, Query and QueryRow and caches the returns *sql.Stmt using the provided
|
||||||
|
// query as the key. So that it can be automatically re-used.
|
||||||
|
type StmtCache struct {
|
||||||
|
prep Preparer
|
||||||
|
cache map[string]*sql.Stmt
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare delegates down to the underlying Preparer and caches the result
|
||||||
|
// using the provided query as a key
|
||||||
|
func (sc *StmtCache) Prepare(query string) (*sql.Stmt, error) {
|
||||||
|
sc.mu.Lock()
|
||||||
|
defer sc.mu.Unlock()
|
||||||
|
|
||||||
|
stmt, ok := sc.cache[query]
|
||||||
|
if ok {
|
||||||
|
return stmt, nil
|
||||||
|
}
|
||||||
|
stmt, err := sc.prep.Prepare(query)
|
||||||
|
if err == nil {
|
||||||
|
sc.cache[query] = stmt
|
||||||
|
}
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec delegates down to the underlying Preparer using a prepared statement
|
||||||
|
func (sc *StmtCache) Exec(query string, args ...interface{}) (res sql.Result, err error) {
|
||||||
|
stmt, err := sc.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return stmt.Exec(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query delegates down to the underlying Preparer using a prepared statement
|
||||||
|
func (sc *StmtCache) Query(query string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||||
|
stmt, err := sc.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return stmt.Query(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRow delegates down to the underlying Preparer using a prepared statement
|
||||||
|
func (sc *StmtCache) QueryRow(query string, args ...interface{}) RowScanner {
|
||||||
|
stmt, err := sc.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
return &Row{err: err}
|
||||||
|
}
|
||||||
|
return stmt.QueryRow(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes and closes all the currently cached prepared statements
|
||||||
|
func (sc *StmtCache) Clear() (err error) {
|
||||||
|
sc.mu.Lock()
|
||||||
|
defer sc.mu.Unlock()
|
||||||
|
|
||||||
|
for key, stmt := range sc.cache {
|
||||||
|
delete(sc.cache, key)
|
||||||
|
|
||||||
|
if stmt == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if cerr := stmt.Close(); cerr != nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("one or more Stmt.Close failed; last error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type DBProxyBeginner interface {
|
||||||
|
DBProxy
|
||||||
|
Begin() (*sql.Tx, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type stmtCacheProxy struct {
|
||||||
|
DBProxy
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStmtCacheProxy(db *sql.DB) DBProxyBeginner {
|
||||||
|
return &stmtCacheProxy{DBProxy: NewStmtCache(db), db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *stmtCacheProxy) Begin() (*sql.Tx, error) {
|
||||||
|
return sp.db.Begin()
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrepareerContext is the interface that wraps the Prepare and PrepareContext methods.
|
||||||
|
//
|
||||||
|
// Prepare executes the given query as implemented by database/sql.Prepare.
|
||||||
|
// PrepareContext executes the given query as implemented by database/sql.PrepareContext.
|
||||||
|
type PreparerContext interface {
|
||||||
|
Preparer
|
||||||
|
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DBProxyContext groups the Execer, Queryer, QueryRower and PreparerContext interfaces.
|
||||||
|
type DBProxyContext interface {
|
||||||
|
Execer
|
||||||
|
Queryer
|
||||||
|
QueryRower
|
||||||
|
PreparerContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStmtCache returns a *StmtCache wrapping a PreparerContext that caches Prepared Stmts.
|
||||||
|
//
|
||||||
|
// Stmts are cached based on the string value of their queries.
|
||||||
|
func NewStmtCache(prep PreparerContext) *StmtCache {
|
||||||
|
return &StmtCache{prep: prep, cache: make(map[string]*sql.Stmt)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStmtCacher is deprecated
|
||||||
|
//
|
||||||
|
// Use NewStmtCache instead
|
||||||
|
func NewStmtCacher(prep PreparerContext) DBProxyContext {
|
||||||
|
return NewStmtCache(prep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrepareContext delegates down to the underlying PreparerContext and caches the result
|
||||||
|
// using the provided query as a key
|
||||||
|
func (sc *StmtCache) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
|
||||||
|
ctxPrep, ok := sc.prep.(PreparerContext)
|
||||||
|
if !ok {
|
||||||
|
return nil, NoContextSupport
|
||||||
|
}
|
||||||
|
sc.mu.Lock()
|
||||||
|
defer sc.mu.Unlock()
|
||||||
|
stmt, ok := sc.cache[query]
|
||||||
|
if ok {
|
||||||
|
return stmt, nil
|
||||||
|
}
|
||||||
|
stmt, err := ctxPrep.PrepareContext(ctx, query)
|
||||||
|
if err == nil {
|
||||||
|
sc.cache[query] = stmt
|
||||||
|
}
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecContext delegates down to the underlying PreparerContext using a prepared statement
|
||||||
|
func (sc *StmtCache) ExecContext(ctx context.Context, query string, args ...interface{}) (res sql.Result, err error) {
|
||||||
|
stmt, err := sc.PrepareContext(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return stmt.ExecContext(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryContext delegates down to the underlying PreparerContext using a prepared statement
|
||||||
|
func (sc *StmtCache) QueryContext(ctx context.Context, query string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||||
|
stmt, err := sc.PrepareContext(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return stmt.QueryContext(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowContext delegates down to the underlying PreparerContext using a prepared statement
|
||||||
|
func (sc *StmtCache) QueryRowContext(ctx context.Context, query string, args ...interface{}) RowScanner {
|
||||||
|
stmt, err := sc.PrepareContext(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return &Row{err: err}
|
||||||
|
}
|
||||||
|
return stmt.QueryRowContext(ctx, args...)
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// +build !go1.8
|
||||||
|
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewStmtCacher returns a DBProxy wrapping prep that caches Prepared Stmts.
|
||||||
|
//
|
||||||
|
// Stmts are cached based on the string value of their queries.
|
||||||
|
func NewStmtCache(prep Preparer) *StmtCache {
|
||||||
|
return &StmtCacher{prep: prep, cache: make(map[string]*sql.Stmt)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStmtCacher is deprecated
|
||||||
|
//
|
||||||
|
// Use NewStmtCache instead
|
||||||
|
func NewStmtCacher(prep Preparer) DBProxy {
|
||||||
|
return NewStmtCache(prep)
|
||||||
|
}
|
|
@ -0,0 +1,288 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/lann/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type updateData struct {
|
||||||
|
PlaceholderFormat PlaceholderFormat
|
||||||
|
RunWith BaseRunner
|
||||||
|
Prefixes []Sqlizer
|
||||||
|
Table string
|
||||||
|
SetClauses []setClause
|
||||||
|
From Sqlizer
|
||||||
|
WhereParts []Sqlizer
|
||||||
|
OrderBys []string
|
||||||
|
Limit string
|
||||||
|
Offset string
|
||||||
|
Suffixes []Sqlizer
|
||||||
|
}
|
||||||
|
|
||||||
|
type setClause struct {
|
||||||
|
column string
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *updateData) Exec() (sql.Result, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
return ExecWith(d.RunWith, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *updateData) Query() (*sql.Rows, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
return QueryWith(d.RunWith, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *updateData) QueryRow() RowScanner {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return &Row{err: RunnerNotSet}
|
||||||
|
}
|
||||||
|
queryRower, ok := d.RunWith.(QueryRower)
|
||||||
|
if !ok {
|
||||||
|
return &Row{err: RunnerNotQueryRunner}
|
||||||
|
}
|
||||||
|
return QueryRowWith(queryRower, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *updateData) ToSql() (sqlStr string, args []interface{}, err error) {
|
||||||
|
if len(d.Table) == 0 {
|
||||||
|
err = fmt.Errorf("update statements must specify a table")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(d.SetClauses) == 0 {
|
||||||
|
err = fmt.Errorf("update statements must have at least one Set clause")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sql := &bytes.Buffer{}
|
||||||
|
|
||||||
|
if len(d.Prefixes) > 0 {
|
||||||
|
args, err = appendToSql(d.Prefixes, sql, " ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.WriteString("UPDATE ")
|
||||||
|
sql.WriteString(d.Table)
|
||||||
|
|
||||||
|
sql.WriteString(" SET ")
|
||||||
|
setSqls := make([]string, len(d.SetClauses))
|
||||||
|
for i, setClause := range d.SetClauses {
|
||||||
|
var valSql string
|
||||||
|
if vs, ok := setClause.value.(Sqlizer); ok {
|
||||||
|
vsql, vargs, err := vs.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
if _, ok := vs.(SelectBuilder); ok {
|
||||||
|
valSql = fmt.Sprintf("(%s)", vsql)
|
||||||
|
} else {
|
||||||
|
valSql = vsql
|
||||||
|
}
|
||||||
|
args = append(args, vargs...)
|
||||||
|
} else {
|
||||||
|
valSql = "?"
|
||||||
|
args = append(args, setClause.value)
|
||||||
|
}
|
||||||
|
setSqls[i] = fmt.Sprintf("%s = %s", setClause.column, valSql)
|
||||||
|
}
|
||||||
|
sql.WriteString(strings.Join(setSqls, ", "))
|
||||||
|
|
||||||
|
if d.From != nil {
|
||||||
|
sql.WriteString(" FROM ")
|
||||||
|
args, err = appendToSql([]Sqlizer{d.From}, sql, "", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.WhereParts) > 0 {
|
||||||
|
sql.WriteString(" WHERE ")
|
||||||
|
args, err = appendToSql(d.WhereParts, sql, " AND ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.OrderBys) > 0 {
|
||||||
|
sql.WriteString(" ORDER BY ")
|
||||||
|
sql.WriteString(strings.Join(d.OrderBys, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Limit) > 0 {
|
||||||
|
sql.WriteString(" LIMIT ")
|
||||||
|
sql.WriteString(d.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Offset) > 0 {
|
||||||
|
sql.WriteString(" OFFSET ")
|
||||||
|
sql.WriteString(d.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(d.Suffixes) > 0 {
|
||||||
|
sql.WriteString(" ")
|
||||||
|
args, err = appendToSql(d.Suffixes, sql, " ", args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builder
|
||||||
|
|
||||||
|
// UpdateBuilder builds SQL UPDATE statements.
|
||||||
|
type UpdateBuilder builder.Builder
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
builder.Register(UpdateBuilder{}, updateData{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format methods
|
||||||
|
|
||||||
|
// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the
|
||||||
|
// query.
|
||||||
|
func (b UpdateBuilder) PlaceholderFormat(f PlaceholderFormat) UpdateBuilder {
|
||||||
|
return builder.Set(b, "PlaceholderFormat", f).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runner methods
|
||||||
|
|
||||||
|
// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec.
|
||||||
|
func (b UpdateBuilder) RunWith(runner BaseRunner) UpdateBuilder {
|
||||||
|
return setRunWith(b, runner).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec builds and Execs the query with the Runner set by RunWith.
|
||||||
|
func (b UpdateBuilder) Exec() (sql.Result, error) {
|
||||||
|
data := builder.GetStruct(b).(updateData)
|
||||||
|
return data.Exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b UpdateBuilder) Query() (*sql.Rows, error) {
|
||||||
|
data := builder.GetStruct(b).(updateData)
|
||||||
|
return data.Query()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b UpdateBuilder) QueryRow() RowScanner {
|
||||||
|
data := builder.GetStruct(b).(updateData)
|
||||||
|
return data.QueryRow()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b UpdateBuilder) Scan(dest ...interface{}) error {
|
||||||
|
return b.QueryRow().Scan(dest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SQL methods
|
||||||
|
|
||||||
|
// ToSql builds the query into a SQL string and bound args.
|
||||||
|
func (b UpdateBuilder) ToSql() (string, []interface{}, error) {
|
||||||
|
data := builder.GetStruct(b).(updateData)
|
||||||
|
return data.ToSql()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustSql builds the query into a SQL string and bound args.
|
||||||
|
// It panics if there are any errors.
|
||||||
|
func (b UpdateBuilder) MustSql() (string, []interface{}) {
|
||||||
|
sql, args, err := b.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sql, args
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix adds an expression to the beginning of the query
|
||||||
|
func (b UpdateBuilder) Prefix(sql string, args ...interface{}) UpdateBuilder {
|
||||||
|
return b.PrefixExpr(Expr(sql, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrefixExpr adds an expression to the very beginning of the query
|
||||||
|
func (b UpdateBuilder) PrefixExpr(expr Sqlizer) UpdateBuilder {
|
||||||
|
return builder.Append(b, "Prefixes", expr).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Table sets the table to be updated.
|
||||||
|
func (b UpdateBuilder) Table(table string) UpdateBuilder {
|
||||||
|
return builder.Set(b, "Table", table).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set adds SET clauses to the query.
|
||||||
|
func (b UpdateBuilder) Set(column string, value interface{}) UpdateBuilder {
|
||||||
|
return builder.Append(b, "SetClauses", setClause{column: column, value: value}).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMap is a convenience method which calls .Set for each key/value pair in clauses.
|
||||||
|
func (b UpdateBuilder) SetMap(clauses map[string]interface{}) UpdateBuilder {
|
||||||
|
keys := make([]string, len(clauses))
|
||||||
|
i := 0
|
||||||
|
for key := range clauses {
|
||||||
|
keys[i] = key
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
for _, key := range keys {
|
||||||
|
val, _ := clauses[key]
|
||||||
|
b = b.Set(key, val)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// From adds FROM clause to the query
|
||||||
|
// FROM is valid construct in postgresql only.
|
||||||
|
func (b UpdateBuilder) From(from string) UpdateBuilder {
|
||||||
|
return builder.Set(b, "From", newPart(from)).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromSelect sets a subquery into the FROM clause of the query.
|
||||||
|
func (b UpdateBuilder) FromSelect(from SelectBuilder, alias string) UpdateBuilder {
|
||||||
|
// Prevent misnumbered parameters in nested selects (#183).
|
||||||
|
from = from.PlaceholderFormat(Question)
|
||||||
|
return builder.Set(b, "From", Alias(from, alias)).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where adds WHERE expressions to the query.
|
||||||
|
//
|
||||||
|
// See SelectBuilder.Where for more information.
|
||||||
|
func (b UpdateBuilder) Where(pred interface{}, args ...interface{}) UpdateBuilder {
|
||||||
|
return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderBy adds ORDER BY expressions to the query.
|
||||||
|
func (b UpdateBuilder) OrderBy(orderBys ...string) UpdateBuilder {
|
||||||
|
return builder.Extend(b, "OrderBys", orderBys).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit sets a LIMIT clause on the query.
|
||||||
|
func (b UpdateBuilder) Limit(limit uint64) UpdateBuilder {
|
||||||
|
return builder.Set(b, "Limit", fmt.Sprintf("%d", limit)).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset sets a OFFSET clause on the query.
|
||||||
|
func (b UpdateBuilder) Offset(offset uint64) UpdateBuilder {
|
||||||
|
return builder.Set(b, "Offset", fmt.Sprintf("%d", offset)).(UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suffix adds an expression to the end of the query
|
||||||
|
func (b UpdateBuilder) Suffix(sql string, args ...interface{}) UpdateBuilder {
|
||||||
|
return b.SuffixExpr(Expr(sql, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuffixExpr adds an expression to the end of the query
|
||||||
|
func (b UpdateBuilder) SuffixExpr(expr Sqlizer) UpdateBuilder {
|
||||||
|
return builder.Append(b, "Suffixes", expr).(UpdateBuilder)
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/lann/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *updateData) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
ctxRunner, ok := d.RunWith.(ExecerContext)
|
||||||
|
if !ok {
|
||||||
|
return nil, NoContextSupport
|
||||||
|
}
|
||||||
|
return ExecContextWith(ctx, ctxRunner, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *updateData) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return nil, RunnerNotSet
|
||||||
|
}
|
||||||
|
ctxRunner, ok := d.RunWith.(QueryerContext)
|
||||||
|
if !ok {
|
||||||
|
return nil, NoContextSupport
|
||||||
|
}
|
||||||
|
return QueryContextWith(ctx, ctxRunner, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *updateData) QueryRowContext(ctx context.Context) RowScanner {
|
||||||
|
if d.RunWith == nil {
|
||||||
|
return &Row{err: RunnerNotSet}
|
||||||
|
}
|
||||||
|
queryRower, ok := d.RunWith.(QueryRowerContext)
|
||||||
|
if !ok {
|
||||||
|
if _, ok := d.RunWith.(QueryerContext); !ok {
|
||||||
|
return &Row{err: RunnerNotQueryRunner}
|
||||||
|
}
|
||||||
|
return &Row{err: NoContextSupport}
|
||||||
|
}
|
||||||
|
return QueryRowContextWith(ctx, queryRower, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecContext builds and ExecContexts the query with the Runner set by RunWith.
|
||||||
|
func (b UpdateBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||||
|
data := builder.GetStruct(b).(updateData)
|
||||||
|
return data.ExecContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryContext builds and QueryContexts the query with the Runner set by RunWith.
|
||||||
|
func (b UpdateBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||||
|
data := builder.GetStruct(b).(updateData)
|
||||||
|
return data.QueryContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith.
|
||||||
|
func (b UpdateBuilder) QueryRowContext(ctx context.Context) RowScanner {
|
||||||
|
data := builder.GetStruct(b).(updateData)
|
||||||
|
return data.QueryRowContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScanContext is a shortcut for QueryRowContext().Scan.
|
||||||
|
func (b UpdateBuilder) ScanContext(ctx context.Context, dest ...interface{}) error {
|
||||||
|
return b.QueryRowContext(ctx).Scan(dest...)
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package squirrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wherePart part
|
||||||
|
|
||||||
|
func newWherePart(pred interface{}, args ...interface{}) Sqlizer {
|
||||||
|
return &wherePart{pred: pred, args: args}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p wherePart) ToSql() (sql string, args []interface{}, err error) {
|
||||||
|
switch pred := p.pred.(type) {
|
||||||
|
case nil:
|
||||||
|
// no-op
|
||||||
|
case rawSqlizer:
|
||||||
|
return pred.toSqlRaw()
|
||||||
|
case Sqlizer:
|
||||||
|
return pred.ToSql()
|
||||||
|
case map[string]interface{}:
|
||||||
|
return Eq(pred).ToSql()
|
||||||
|
case string:
|
||||||
|
sql = pred
|
||||||
|
args = p.args
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("expected string-keyed map or string, not %T", pred)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -1,150 +0,0 @@
|
||||||
/**********************************************************************
|
|
||||||
* Copyright (c) 2015 Pieter Wuille *
|
|
||||||
* Distributed under the MIT software license, see the accompanying *
|
|
||||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <secp256k1.h>
|
|
||||||
|
|
||||||
#include "lax_der_parsing.h"
|
|
||||||
|
|
||||||
int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
|
|
||||||
size_t rpos, rlen, spos, slen;
|
|
||||||
size_t pos = 0;
|
|
||||||
size_t lenbyte;
|
|
||||||
unsigned char tmpsig[64] = {0};
|
|
||||||
int overflow = 0;
|
|
||||||
|
|
||||||
/* Hack to initialize sig with a correctly-parsed but invalid signature. */
|
|
||||||
secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
|
|
||||||
|
|
||||||
/* Sequence tag byte */
|
|
||||||
if (pos == inputlen || input[pos] != 0x30) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
|
|
||||||
/* Sequence length bytes */
|
|
||||||
if (pos == inputlen) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
lenbyte = input[pos++];
|
|
||||||
if (lenbyte & 0x80) {
|
|
||||||
lenbyte -= 0x80;
|
|
||||||
if (pos + lenbyte > inputlen) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pos += lenbyte;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Integer tag byte for R */
|
|
||||||
if (pos == inputlen || input[pos] != 0x02) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
|
|
||||||
/* Integer length for R */
|
|
||||||
if (pos == inputlen) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
lenbyte = input[pos++];
|
|
||||||
if (lenbyte & 0x80) {
|
|
||||||
lenbyte -= 0x80;
|
|
||||||
if (pos + lenbyte > inputlen) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
while (lenbyte > 0 && input[pos] == 0) {
|
|
||||||
pos++;
|
|
||||||
lenbyte--;
|
|
||||||
}
|
|
||||||
if (lenbyte >= sizeof(size_t)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
rlen = 0;
|
|
||||||
while (lenbyte > 0) {
|
|
||||||
rlen = (rlen << 8) + input[pos];
|
|
||||||
pos++;
|
|
||||||
lenbyte--;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rlen = lenbyte;
|
|
||||||
}
|
|
||||||
if (rlen > inputlen - pos) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
rpos = pos;
|
|
||||||
pos += rlen;
|
|
||||||
|
|
||||||
/* Integer tag byte for S */
|
|
||||||
if (pos == inputlen || input[pos] != 0x02) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
|
|
||||||
/* Integer length for S */
|
|
||||||
if (pos == inputlen) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
lenbyte = input[pos++];
|
|
||||||
if (lenbyte & 0x80) {
|
|
||||||
lenbyte -= 0x80;
|
|
||||||
if (pos + lenbyte > inputlen) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
while (lenbyte > 0 && input[pos] == 0) {
|
|
||||||
pos++;
|
|
||||||
lenbyte--;
|
|
||||||
}
|
|
||||||
if (lenbyte >= sizeof(size_t)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
slen = 0;
|
|
||||||
while (lenbyte > 0) {
|
|
||||||
slen = (slen << 8) + input[pos];
|
|
||||||
pos++;
|
|
||||||
lenbyte--;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
slen = lenbyte;
|
|
||||||
}
|
|
||||||
if (slen > inputlen - pos) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
spos = pos;
|
|
||||||
pos += slen;
|
|
||||||
|
|
||||||
/* Ignore leading zeroes in R */
|
|
||||||
while (rlen > 0 && input[rpos] == 0) {
|
|
||||||
rlen--;
|
|
||||||
rpos++;
|
|
||||||
}
|
|
||||||
/* Copy R value */
|
|
||||||
if (rlen > 32) {
|
|
||||||
overflow = 1;
|
|
||||||
} else {
|
|
||||||
memcpy(tmpsig + 32 - rlen, input + rpos, rlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ignore leading zeroes in S */
|
|
||||||
while (slen > 0 && input[spos] == 0) {
|
|
||||||
slen--;
|
|
||||||
spos++;
|
|
||||||
}
|
|
||||||
/* Copy S value */
|
|
||||||
if (slen > 32) {
|
|
||||||
overflow = 1;
|
|
||||||
} else {
|
|
||||||
memcpy(tmpsig + 64 - slen, input + spos, slen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!overflow) {
|
|
||||||
overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
|
|
||||||
}
|
|
||||||
if (overflow) {
|
|
||||||
memset(tmpsig, 0, 64);
|
|
||||||
secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
/**********************************************************************
|
|
||||||
* Copyright (c) 2015 Pieter Wuille *
|
|
||||||
* Distributed under the MIT software license, see the accompanying *
|
|
||||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
/****
|
|
||||||
* Please do not link this file directly. It is not part of the libsecp256k1
|
|
||||||
* project and does not promise any stability in its API, functionality or
|
|
||||||
* presence. Projects which use this code should instead copy this header
|
|
||||||
* and its accompanying .c file directly into their codebase.
|
|
||||||
****/
|
|
||||||
|
|
||||||
/* This file defines a function that parses DER with various errors and
|
|
||||||
* violations. This is not a part of the library itself, because the allowed
|
|
||||||
* violations are chosen arbitrarily and do not follow or establish any
|
|
||||||
* standard.
|
|
||||||
*
|
|
||||||
* In many places it matters that different implementations do not only accept
|
|
||||||
* the same set of valid signatures, but also reject the same set of signatures.
|
|
||||||
* The only means to accomplish that is by strictly obeying a standard, and not
|
|
||||||
* accepting anything else.
|
|
||||||
*
|
|
||||||
* Nonetheless, sometimes there is a need for compatibility with systems that
|
|
||||||
* use signatures which do not strictly obey DER. The snippet below shows how
|
|
||||||
* certain violations are easily supported. You may need to adapt it.
|
|
||||||
*
|
|
||||||
* Do not use this for new systems. Use well-defined DER or compact signatures
|
|
||||||
* instead if you have the choice (see secp256k1_ecdsa_signature_parse_der and
|
|
||||||
* secp256k1_ecdsa_signature_parse_compact).
|
|
||||||
*
|
|
||||||
* The supported violations are:
|
|
||||||
* - All numbers are parsed as nonnegative integers, even though X.609-0207
|
|
||||||
* section 8.3.3 specifies that integers are always encoded as two's
|
|
||||||
* complement.
|
|
||||||
* - Integers can have length 0, even though section 8.3.1 says they can't.
|
|
||||||
* - Integers with overly long padding are accepted, violation section
|
|
||||||
* 8.3.2.
|
|
||||||
* - 127-byte long length descriptors are accepted, even though section
|
|
||||||
* 8.1.3.5.c says that they are not.
|
|
||||||
* - Trailing garbage data inside or after the signature is ignored.
|
|
||||||
* - The length descriptor of the sequence is ignored.
|
|
||||||
*
|
|
||||||
* Compared to for example OpenSSL, many violations are NOT supported:
|
|
||||||
* - Using overly long tag descriptors for the sequence or integers inside,
|
|
||||||
* violating section 8.1.2.2.
|
|
||||||
* - Encoding primitive integers as constructed values, violating section
|
|
||||||
* 8.3.1.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
|
|
||||||
#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
|
|
||||||
|
|
||||||
#include <secp256k1.h>
|
|
||||||
|
|
||||||
# ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
# endif
|
|
||||||
|
|
||||||
/** Parse a signature in "lax DER" format
|
|
||||||
*
|
|
||||||
* Returns: 1 when the signature could be parsed, 0 otherwise.
|
|
||||||
* Args: ctx: a secp256k1 context object
|
|
||||||
* Out: sig: a pointer to a signature object
|
|
||||||
* In: input: a pointer to the signature to be parsed
|
|
||||||
* inputlen: the length of the array pointed to be input
|
|
||||||
*
|
|
||||||
* This function will accept any valid DER encoded signature, even if the
|
|
||||||
* encoded numbers are out of range. In addition, it will accept signatures
|
|
||||||
* which violate the DER spec in various ways. Its purpose is to allow
|
|
||||||
* validation of the Bitcoin blockchain, which includes non-DER signatures
|
|
||||||
* from before the network rules were updated to enforce DER. Note that
|
|
||||||
* the set of supported violations is a strict subset of what OpenSSL will
|
|
||||||
* accept.
|
|
||||||
*
|
|
||||||
* After the call, sig will always be initialized. If parsing failed or the
|
|
||||||
* encoded numbers are out of range, signature validation with it is
|
|
||||||
* guaranteed to fail for every message and public key.
|
|
||||||
*/
|
|
||||||
int ecdsa_signature_parse_der_lax(
|
|
||||||
const secp256k1_context* ctx,
|
|
||||||
secp256k1_ecdsa_signature* sig,
|
|
||||||
const unsigned char *input,
|
|
||||||
size_t inputlen
|
|
||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,113 +0,0 @@
|
||||||
/**********************************************************************
|
|
||||||
* Copyright (c) 2014, 2015 Pieter Wuille *
|
|
||||||
* Distributed under the MIT software license, see the accompanying *
|
|
||||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <secp256k1.h>
|
|
||||||
|
|
||||||
#include "lax_der_privatekey_parsing.h"
|
|
||||||
|
|
||||||
int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) {
|
|
||||||
const unsigned char *end = privkey + privkeylen;
|
|
||||||
int lenb = 0;
|
|
||||||
int len = 0;
|
|
||||||
memset(out32, 0, 32);
|
|
||||||
/* sequence header */
|
|
||||||
if (end < privkey+1 || *privkey != 0x30) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
privkey++;
|
|
||||||
/* sequence length constructor */
|
|
||||||
if (end < privkey+1 || !(*privkey & 0x80)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
lenb = *privkey & ~0x80; privkey++;
|
|
||||||
if (lenb < 1 || lenb > 2) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (end < privkey+lenb) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* sequence length */
|
|
||||||
len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0);
|
|
||||||
privkey += lenb;
|
|
||||||
if (end < privkey+len) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* sequence element 0: version number (=1) */
|
|
||||||
if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
privkey += 3;
|
|
||||||
/* sequence element 1: octet string, up to 32 bytes */
|
|
||||||
if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]);
|
|
||||||
if (!secp256k1_ec_seckey_verify(ctx, out32)) {
|
|
||||||
memset(out32, 0, 32);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) {
|
|
||||||
secp256k1_pubkey pubkey;
|
|
||||||
size_t pubkeylen = 0;
|
|
||||||
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) {
|
|
||||||
*privkeylen = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (compressed) {
|
|
||||||
static const unsigned char begin[] = {
|
|
||||||
0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20
|
|
||||||
};
|
|
||||||
static const unsigned char middle[] = {
|
|
||||||
0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
|
|
||||||
0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
||||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
||||||
0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
|
|
||||||
0x21,0x02,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
|
|
||||||
0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
|
|
||||||
0x17,0x98,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
||||||
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
|
|
||||||
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00
|
|
||||||
};
|
|
||||||
unsigned char *ptr = privkey;
|
|
||||||
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
|
|
||||||
memcpy(ptr, key32, 32); ptr += 32;
|
|
||||||
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
|
|
||||||
pubkeylen = 33;
|
|
||||||
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED);
|
|
||||||
ptr += pubkeylen;
|
|
||||||
*privkeylen = ptr - privkey;
|
|
||||||
} else {
|
|
||||||
static const unsigned char begin[] = {
|
|
||||||
0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20
|
|
||||||
};
|
|
||||||
static const unsigned char middle[] = {
|
|
||||||
0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
|
|
||||||
0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
||||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
||||||
0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
|
|
||||||
0x41,0x04,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
|
|
||||||
0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
|
|
||||||
0x17,0x98,0x48,0x3A,0xDA,0x77,0x26,0xA3,0xC4,0x65,0x5D,0xA4,0xFB,0xFC,0x0E,0x11,
|
|
||||||
0x08,0xA8,0xFD,0x17,0xB4,0x48,0xA6,0x85,0x54,0x19,0x9C,0x47,0xD0,0x8F,0xFB,0x10,
|
|
||||||
0xD4,0xB8,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
||||||
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
|
|
||||||
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00
|
|
||||||
};
|
|
||||||
unsigned char *ptr = privkey;
|
|
||||||
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
|
|
||||||
memcpy(ptr, key32, 32); ptr += 32;
|
|
||||||
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
|
|
||||||
pubkeylen = 65;
|
|
||||||
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
|
|
||||||
ptr += pubkeylen;
|
|
||||||
*privkeylen = ptr - privkey;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
/**********************************************************************
|
|
||||||
* Copyright (c) 2014, 2015 Pieter Wuille *
|
|
||||||
* Distributed under the MIT software license, see the accompanying *
|
|
||||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
/****
|
|
||||||
* Please do not link this file directly. It is not part of the libsecp256k1
|
|
||||||
* project and does not promise any stability in its API, functionality or
|
|
||||||
* presence. Projects which use this code should instead copy this header
|
|
||||||
* and its accompanying .c file directly into their codebase.
|
|
||||||
****/
|
|
||||||
|
|
||||||
/* This file contains code snippets that parse DER private keys with
|
|
||||||
* various errors and violations. This is not a part of the library
|
|
||||||
* itself, because the allowed violations are chosen arbitrarily and
|
|
||||||
* do not follow or establish any standard.
|
|
||||||
*
|
|
||||||
* It also contains code to serialize private keys in a compatible
|
|
||||||
* manner.
|
|
||||||
*
|
|
||||||
* These functions are meant for compatibility with applications
|
|
||||||
* that require BER encoded keys. When working with secp256k1-specific
|
|
||||||
* code, the simple 32-byte private keys normally used by the
|
|
||||||
* library are sufficient.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
|
|
||||||
#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
|
|
||||||
|
|
||||||
#include <secp256k1.h>
|
|
||||||
|
|
||||||
# ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
# endif
|
|
||||||
|
|
||||||
/** Export a private key in DER format.
|
|
||||||
*
|
|
||||||
* Returns: 1 if the private key was valid.
|
|
||||||
* Args: ctx: pointer to a context object, initialized for signing (cannot
|
|
||||||
* be NULL)
|
|
||||||
* Out: privkey: pointer to an array for storing the private key in BER.
|
|
||||||
* Should have space for 279 bytes, and cannot be NULL.
|
|
||||||
* privkeylen: Pointer to an int where the length of the private key in
|
|
||||||
* privkey will be stored.
|
|
||||||
* In: seckey: pointer to a 32-byte secret key to export.
|
|
||||||
* compressed: 1 if the key should be exported in
|
|
||||||
* compressed format, 0 otherwise
|
|
||||||
*
|
|
||||||
* This function is purely meant for compatibility with applications that
|
|
||||||
* require BER encoded keys. When working with secp256k1-specific code, the
|
|
||||||
* simple 32-byte private keys are sufficient.
|
|
||||||
*
|
|
||||||
* Note that this function does not guarantee correct DER output. It is
|
|
||||||
* guaranteed to be parsable by secp256k1_ec_privkey_import_der
|
|
||||||
*/
|
|
||||||
SECP256K1_WARN_UNUSED_RESULT int ec_privkey_export_der(
|
|
||||||
const secp256k1_context* ctx,
|
|
||||||
unsigned char *privkey,
|
|
||||||
size_t *privkeylen,
|
|
||||||
const unsigned char *seckey,
|
|
||||||
int compressed
|
|
||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Import a private key in DER format.
|
|
||||||
* Returns: 1 if a private key was extracted.
|
|
||||||
* Args: ctx: pointer to a context object (cannot be NULL).
|
|
||||||
* Out: seckey: pointer to a 32-byte array for storing the private key.
|
|
||||||
* (cannot be NULL).
|
|
||||||
* In: privkey: pointer to a private key in DER format (cannot be NULL).
|
|
||||||
* privkeylen: length of the DER private key pointed to be privkey.
|
|
||||||
*
|
|
||||||
* This function will accept more than just strict DER, and even allow some BER
|
|
||||||
* violations. The public key stored inside the DER-encoded private key is not
|
|
||||||
* verified for correctness, nor are the curve parameters. Use this function
|
|
||||||
* only if you know in advance it is supposed to contain a secp256k1 private
|
|
||||||
* key.
|
|
||||||
*/
|
|
||||||
SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der(
|
|
||||||
const secp256k1_context* ctx,
|
|
||||||
unsigned char *seckey,
|
|
||||||
const unsigned char *privkey,
|
|
||||||
size_t privkeylen
|
|
||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,377 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "org_bitcoin_NativeSecp256k1.h"
|
|
||||||
#include "include/secp256k1.h"
|
|
||||||
#include "include/secp256k1_ecdh.h"
|
|
||||||
#include "include/secp256k1_recovery.h"
|
|
||||||
|
|
||||||
|
|
||||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
|
|
||||||
(JNIEnv* env, jclass classObject, jlong ctx_l)
|
|
||||||
{
|
|
||||||
const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
|
|
||||||
jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx);
|
|
||||||
|
|
||||||
(void)classObject;(void)env;
|
|
||||||
|
|
||||||
return ctx_clone_l;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
|
|
||||||
const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
|
||||||
|
|
||||||
(void)classObject;
|
|
||||||
|
|
||||||
return secp256k1_context_randomize(ctx, seed);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
|
|
||||||
(JNIEnv* env, jclass classObject, jlong ctx_l)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
|
|
||||||
secp256k1_context_destroy(ctx);
|
|
||||||
|
|
||||||
(void)classObject;(void)env;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
|
|
||||||
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
|
||||||
const unsigned char* sigdata = { (unsigned char*) (data + 32) };
|
|
||||||
const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) };
|
|
||||||
|
|
||||||
secp256k1_ecdsa_signature sig;
|
|
||||||
secp256k1_pubkey pubkey;
|
|
||||||
|
|
||||||
int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen);
|
|
||||||
|
|
||||||
if( ret ) {
|
|
||||||
ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
|
|
||||||
|
|
||||||
if( ret ) {
|
|
||||||
ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)classObject;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
|
||||||
unsigned char* secKey = (unsigned char*) (data + 32);
|
|
||||||
|
|
||||||
jobjectArray retArray;
|
|
||||||
jbyteArray sigArray, intsByteArray;
|
|
||||||
unsigned char intsarray[2];
|
|
||||||
|
|
||||||
secp256k1_ecdsa_signature sig[72];
|
|
||||||
|
|
||||||
int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL );
|
|
||||||
|
|
||||||
unsigned char outputSer[72];
|
|
||||||
size_t outputLen = 72;
|
|
||||||
|
|
||||||
if( ret ) {
|
|
||||||
int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2;
|
|
||||||
}
|
|
||||||
|
|
||||||
intsarray[0] = outputLen;
|
|
||||||
intsarray[1] = ret;
|
|
||||||
|
|
||||||
retArray = (*env)->NewObjectArray(env, 2,
|
|
||||||
(*env)->FindClass(env, "[B"),
|
|
||||||
(*env)->NewByteArray(env, 1));
|
|
||||||
|
|
||||||
sigArray = (*env)->NewByteArray(env, outputLen);
|
|
||||||
(*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 0, sigArray);
|
|
||||||
|
|
||||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
|
||||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
|
||||||
|
|
||||||
(void)classObject;
|
|
||||||
|
|
||||||
return retArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
|
||||||
|
|
||||||
(void)classObject;
|
|
||||||
|
|
||||||
return secp256k1_ec_seckey_verify(ctx, secKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
|
||||||
|
|
||||||
secp256k1_pubkey pubkey;
|
|
||||||
|
|
||||||
jobjectArray retArray;
|
|
||||||
jbyteArray pubkeyArray, intsByteArray;
|
|
||||||
unsigned char intsarray[2];
|
|
||||||
|
|
||||||
int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey);
|
|
||||||
|
|
||||||
unsigned char outputSer[65];
|
|
||||||
size_t outputLen = 65;
|
|
||||||
|
|
||||||
if( ret ) {
|
|
||||||
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
|
|
||||||
}
|
|
||||||
|
|
||||||
intsarray[0] = outputLen;
|
|
||||||
intsarray[1] = ret;
|
|
||||||
|
|
||||||
retArray = (*env)->NewObjectArray(env, 2,
|
|
||||||
(*env)->FindClass(env, "[B"),
|
|
||||||
(*env)->NewByteArray(env, 1));
|
|
||||||
|
|
||||||
pubkeyArray = (*env)->NewByteArray(env, outputLen);
|
|
||||||
(*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray);
|
|
||||||
|
|
||||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
|
||||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
|
||||||
|
|
||||||
(void)classObject;
|
|
||||||
|
|
||||||
return retArray;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
|
||||||
const unsigned char* tweak = (unsigned char*) (privkey + 32);
|
|
||||||
|
|
||||||
jobjectArray retArray;
|
|
||||||
jbyteArray privArray, intsByteArray;
|
|
||||||
unsigned char intsarray[2];
|
|
||||||
|
|
||||||
int privkeylen = 32;
|
|
||||||
|
|
||||||
int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak);
|
|
||||||
|
|
||||||
intsarray[0] = privkeylen;
|
|
||||||
intsarray[1] = ret;
|
|
||||||
|
|
||||||
retArray = (*env)->NewObjectArray(env, 2,
|
|
||||||
(*env)->FindClass(env, "[B"),
|
|
||||||
(*env)->NewByteArray(env, 1));
|
|
||||||
|
|
||||||
privArray = (*env)->NewByteArray(env, privkeylen);
|
|
||||||
(*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 0, privArray);
|
|
||||||
|
|
||||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
|
||||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
|
||||||
|
|
||||||
(void)classObject;
|
|
||||||
|
|
||||||
return retArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
|
||||||
const unsigned char* tweak = (unsigned char*) (privkey + 32);
|
|
||||||
|
|
||||||
jobjectArray retArray;
|
|
||||||
jbyteArray privArray, intsByteArray;
|
|
||||||
unsigned char intsarray[2];
|
|
||||||
|
|
||||||
int privkeylen = 32;
|
|
||||||
|
|
||||||
int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak);
|
|
||||||
|
|
||||||
intsarray[0] = privkeylen;
|
|
||||||
intsarray[1] = ret;
|
|
||||||
|
|
||||||
retArray = (*env)->NewObjectArray(env, 2,
|
|
||||||
(*env)->FindClass(env, "[B"),
|
|
||||||
(*env)->NewByteArray(env, 1));
|
|
||||||
|
|
||||||
privArray = (*env)->NewByteArray(env, privkeylen);
|
|
||||||
(*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 0, privArray);
|
|
||||||
|
|
||||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
|
||||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
|
||||||
|
|
||||||
(void)classObject;
|
|
||||||
|
|
||||||
return retArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/
|
|
||||||
unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
|
||||||
const unsigned char* tweak = (unsigned char*) (pkey + publen);
|
|
||||||
|
|
||||||
jobjectArray retArray;
|
|
||||||
jbyteArray pubArray, intsByteArray;
|
|
||||||
unsigned char intsarray[2];
|
|
||||||
unsigned char outputSer[65];
|
|
||||||
size_t outputLen = 65;
|
|
||||||
|
|
||||||
secp256k1_pubkey pubkey;
|
|
||||||
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
|
|
||||||
|
|
||||||
if( ret ) {
|
|
||||||
ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ret ) {
|
|
||||||
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
|
|
||||||
}
|
|
||||||
|
|
||||||
intsarray[0] = outputLen;
|
|
||||||
intsarray[1] = ret;
|
|
||||||
|
|
||||||
retArray = (*env)->NewObjectArray(env, 2,
|
|
||||||
(*env)->FindClass(env, "[B"),
|
|
||||||
(*env)->NewByteArray(env, 1));
|
|
||||||
|
|
||||||
pubArray = (*env)->NewByteArray(env, outputLen);
|
|
||||||
(*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
|
|
||||||
|
|
||||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
|
||||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
|
||||||
|
|
||||||
(void)classObject;
|
|
||||||
|
|
||||||
return retArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
|
||||||
const unsigned char* tweak = (unsigned char*) (pkey + publen);
|
|
||||||
|
|
||||||
jobjectArray retArray;
|
|
||||||
jbyteArray pubArray, intsByteArray;
|
|
||||||
unsigned char intsarray[2];
|
|
||||||
unsigned char outputSer[65];
|
|
||||||
size_t outputLen = 65;
|
|
||||||
|
|
||||||
secp256k1_pubkey pubkey;
|
|
||||||
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
|
|
||||||
|
|
||||||
if ( ret ) {
|
|
||||||
ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ret ) {
|
|
||||||
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
|
|
||||||
}
|
|
||||||
|
|
||||||
intsarray[0] = outputLen;
|
|
||||||
intsarray[1] = ret;
|
|
||||||
|
|
||||||
retArray = (*env)->NewObjectArray(env, 2,
|
|
||||||
(*env)->FindClass(env, "[B"),
|
|
||||||
(*env)->NewByteArray(env, 1));
|
|
||||||
|
|
||||||
pubArray = (*env)->NewByteArray(env, outputLen);
|
|
||||||
(*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
|
|
||||||
|
|
||||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
|
||||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
|
||||||
|
|
||||||
(void)classObject;
|
|
||||||
|
|
||||||
return retArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine
|
|
||||||
(JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys)
|
|
||||||
{
|
|
||||||
(void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
|
||||||
const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
|
||||||
const unsigned char* pubdata = (const unsigned char*) (secdata + 32);
|
|
||||||
|
|
||||||
jobjectArray retArray;
|
|
||||||
jbyteArray outArray, intsByteArray;
|
|
||||||
unsigned char intsarray[1];
|
|
||||||
secp256k1_pubkey pubkey;
|
|
||||||
unsigned char nonce_res[32];
|
|
||||||
size_t outputLen = 32;
|
|
||||||
|
|
||||||
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
|
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
ret = secp256k1_ecdh(
|
|
||||||
ctx,
|
|
||||||
nonce_res,
|
|
||||||
&pubkey,
|
|
||||||
secdata
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
intsarray[0] = ret;
|
|
||||||
|
|
||||||
retArray = (*env)->NewObjectArray(env, 2,
|
|
||||||
(*env)->FindClass(env, "[B"),
|
|
||||||
(*env)->NewByteArray(env, 1));
|
|
||||||
|
|
||||||
outArray = (*env)->NewByteArray(env, outputLen);
|
|
||||||
(*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 0, outArray);
|
|
||||||
|
|
||||||
intsByteArray = (*env)->NewByteArray(env, 1);
|
|
||||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray);
|
|
||||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
|
||||||
|
|
||||||
(void)classObject;
|
|
||||||
|
|
||||||
return retArray;
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
|
||||||
#include <jni.h>
|
|
||||||
#include "include/secp256k1.h"
|
|
||||||
/* Header for class org_bitcoin_NativeSecp256k1 */
|
|
||||||
|
|
||||||
#ifndef _Included_org_bitcoin_NativeSecp256k1
|
|
||||||
#define _Included_org_bitcoin_NativeSecp256k1
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_ctx_clone
|
|
||||||
* Signature: (J)J
|
|
||||||
*/
|
|
||||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
|
|
||||||
(JNIEnv *, jclass, jlong);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_context_randomize
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;J)I
|
|
||||||
*/
|
|
||||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
|
|
||||||
(JNIEnv *, jclass, jobject, jlong);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_privkey_tweak_add
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
|
||||||
*/
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
|
|
||||||
(JNIEnv *, jclass, jobject, jlong);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_privkey_tweak_mul
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
|
||||||
*/
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
|
|
||||||
(JNIEnv *, jclass, jobject, jlong);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_pubkey_tweak_add
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
|
||||||
*/
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
|
|
||||||
(JNIEnv *, jclass, jobject, jlong, jint);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_pubkey_tweak_mul
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
|
||||||
*/
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
|
|
||||||
(JNIEnv *, jclass, jobject, jlong, jint);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_destroy_context
|
|
||||||
* Signature: (J)V
|
|
||||||
*/
|
|
||||||
SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
|
|
||||||
(JNIEnv *, jclass, jlong);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_ecdsa_verify
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;JII)I
|
|
||||||
*/
|
|
||||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
|
|
||||||
(JNIEnv *, jclass, jobject, jlong, jint, jint);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_ecdsa_sign
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
|
||||||
*/
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
|
|
||||||
(JNIEnv *, jclass, jobject, jlong);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_ec_seckey_verify
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;J)I
|
|
||||||
*/
|
|
||||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
|
|
||||||
(JNIEnv *, jclass, jobject, jlong);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_ec_pubkey_create
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
|
||||||
*/
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
|
|
||||||
(JNIEnv *, jclass, jobject, jlong);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_ec_pubkey_parse
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
|
||||||
*/
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse
|
|
||||||
(JNIEnv *, jclass, jobject, jlong, jint);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_NativeSecp256k1
|
|
||||||
* Method: secp256k1_ecdh
|
|
||||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
|
||||||
*/
|
|
||||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
|
|
||||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
|
@ -1,15 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "org_bitcoin_Secp256k1Context.h"
|
|
||||||
#include "include/secp256k1.h"
|
|
||||||
|
|
||||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
|
|
||||||
(JNIEnv* env, jclass classObject)
|
|
||||||
{
|
|
||||||
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
|
||||||
|
|
||||||
(void)classObject;(void)env;
|
|
||||||
|
|
||||||
return (uintptr_t)ctx;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
|
||||||
#include <jni.h>
|
|
||||||
#include "include/secp256k1.h"
|
|
||||||
/* Header for class org_bitcoin_Secp256k1Context */
|
|
||||||
|
|
||||||
#ifndef _Included_org_bitcoin_Secp256k1Context
|
|
||||||
#define _Included_org_bitcoin_Secp256k1Context
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
* Class: org_bitcoin_Secp256k1Context
|
|
||||||
* Method: secp256k1_init_context
|
|
||||||
* Signature: ()J
|
|
||||||
*/
|
|
||||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
|
|
||||||
(JNIEnv *, jclass);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
|
@ -1,54 +0,0 @@
|
||||||
/**********************************************************************
|
|
||||||
* Copyright (c) 2015 Andrew Poelstra *
|
|
||||||
* Distributed under the MIT software license, see the accompanying *
|
|
||||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
#ifndef _SECP256K1_MODULE_ECDH_MAIN_
|
|
||||||
#define _SECP256K1_MODULE_ECDH_MAIN_
|
|
||||||
|
|
||||||
#include "include/secp256k1_ecdh.h"
|
|
||||||
#include "ecmult_const_impl.h"
|
|
||||||
|
|
||||||
int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) {
|
|
||||||
int ret = 0;
|
|
||||||
int overflow = 0;
|
|
||||||
secp256k1_gej res;
|
|
||||||
secp256k1_ge pt;
|
|
||||||
secp256k1_scalar s;
|
|
||||||
VERIFY_CHECK(ctx != NULL);
|
|
||||||
ARG_CHECK(result != NULL);
|
|
||||||
ARG_CHECK(point != NULL);
|
|
||||||
ARG_CHECK(scalar != NULL);
|
|
||||||
|
|
||||||
secp256k1_pubkey_load(ctx, &pt, point);
|
|
||||||
secp256k1_scalar_set_b32(&s, scalar, &overflow);
|
|
||||||
if (overflow || secp256k1_scalar_is_zero(&s)) {
|
|
||||||
ret = 0;
|
|
||||||
} else {
|
|
||||||
unsigned char x[32];
|
|
||||||
unsigned char y[1];
|
|
||||||
secp256k1_sha256_t sha;
|
|
||||||
|
|
||||||
secp256k1_ecmult_const(&res, &pt, &s);
|
|
||||||
secp256k1_ge_set_gej(&pt, &res);
|
|
||||||
/* Compute a hash of the point in compressed form
|
|
||||||
* Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not
|
|
||||||
* expect its output to be secret and has a timing sidechannel. */
|
|
||||||
secp256k1_fe_normalize(&pt.x);
|
|
||||||
secp256k1_fe_normalize(&pt.y);
|
|
||||||
secp256k1_fe_get_b32(x, &pt.x);
|
|
||||||
y[0] = 0x02 | secp256k1_fe_is_odd(&pt.y);
|
|
||||||
|
|
||||||
secp256k1_sha256_initialize(&sha);
|
|
||||||
secp256k1_sha256_write(&sha, y, sizeof(y));
|
|
||||||
secp256k1_sha256_write(&sha, x, sizeof(x));
|
|
||||||
secp256k1_sha256_finalize(&sha, result);
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
secp256k1_scalar_clear(&s);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,105 +0,0 @@
|
||||||
/**********************************************************************
|
|
||||||
* Copyright (c) 2015 Andrew Poelstra *
|
|
||||||
* Distributed under the MIT software license, see the accompanying *
|
|
||||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
#ifndef _SECP256K1_MODULE_ECDH_TESTS_
|
|
||||||
#define _SECP256K1_MODULE_ECDH_TESTS_
|
|
||||||
|
|
||||||
void test_ecdh_api(void) {
|
|
||||||
/* Setup context that just counts errors */
|
|
||||||
secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
|
||||||
secp256k1_pubkey point;
|
|
||||||
unsigned char res[32];
|
|
||||||
unsigned char s_one[32] = { 0 };
|
|
||||||
int32_t ecount = 0;
|
|
||||||
s_one[31] = 1;
|
|
||||||
|
|
||||||
secp256k1_context_set_error_callback(tctx, counting_illegal_callback_fn, &ecount);
|
|
||||||
secp256k1_context_set_illegal_callback(tctx, counting_illegal_callback_fn, &ecount);
|
|
||||||
CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1);
|
|
||||||
|
|
||||||
/* Check all NULLs are detected */
|
|
||||||
CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1);
|
|
||||||
CHECK(ecount == 0);
|
|
||||||
CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0);
|
|
||||||
CHECK(ecount == 1);
|
|
||||||
CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0);
|
|
||||||
CHECK(ecount == 2);
|
|
||||||
CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0);
|
|
||||||
CHECK(ecount == 3);
|
|
||||||
CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1);
|
|
||||||
CHECK(ecount == 3);
|
|
||||||
|
|
||||||
/* Cleanup */
|
|
||||||
secp256k1_context_destroy(tctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_ecdh_generator_basepoint(void) {
|
|
||||||
unsigned char s_one[32] = { 0 };
|
|
||||||
secp256k1_pubkey point[2];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
s_one[31] = 1;
|
|
||||||
/* Check against pubkey creation when the basepoint is the generator */
|
|
||||||
for (i = 0; i < 100; ++i) {
|
|
||||||
secp256k1_sha256_t sha;
|
|
||||||
unsigned char s_b32[32];
|
|
||||||
unsigned char output_ecdh[32];
|
|
||||||
unsigned char output_ser[32];
|
|
||||||
unsigned char point_ser[33];
|
|
||||||
size_t point_ser_len = sizeof(point_ser);
|
|
||||||
secp256k1_scalar s;
|
|
||||||
|
|
||||||
random_scalar_order(&s);
|
|
||||||
secp256k1_scalar_get_b32(s_b32, &s);
|
|
||||||
|
|
||||||
/* compute using ECDH function */
|
|
||||||
CHECK(secp256k1_ec_pubkey_create(ctx, &point[0], s_one) == 1);
|
|
||||||
CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32) == 1);
|
|
||||||
/* compute "explicitly" */
|
|
||||||
CHECK(secp256k1_ec_pubkey_create(ctx, &point[1], s_b32) == 1);
|
|
||||||
CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1);
|
|
||||||
CHECK(point_ser_len == sizeof(point_ser));
|
|
||||||
secp256k1_sha256_initialize(&sha);
|
|
||||||
secp256k1_sha256_write(&sha, point_ser, point_ser_len);
|
|
||||||
secp256k1_sha256_finalize(&sha, output_ser);
|
|
||||||
/* compare */
|
|
||||||
CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_bad_scalar(void) {
|
|
||||||
unsigned char s_zero[32] = { 0 };
|
|
||||||
unsigned char s_overflow[32] = {
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
|
||||||
0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
|
|
||||||
0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41
|
|
||||||
};
|
|
||||||
unsigned char s_rand[32] = { 0 };
|
|
||||||
unsigned char output[32];
|
|
||||||
secp256k1_scalar rand;
|
|
||||||
secp256k1_pubkey point;
|
|
||||||
|
|
||||||
/* Create random point */
|
|
||||||
random_scalar_order(&rand);
|
|
||||||
secp256k1_scalar_get_b32(s_rand, &rand);
|
|
||||||
CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_rand) == 1);
|
|
||||||
|
|
||||||
/* Try to multiply it by bad values */
|
|
||||||
CHECK(secp256k1_ecdh(ctx, output, &point, s_zero) == 0);
|
|
||||||
CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 0);
|
|
||||||
/* ...and a good one */
|
|
||||||
s_overflow[31] -= 1;
|
|
||||||
CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_ecdh_tests(void) {
|
|
||||||
test_ecdh_api();
|
|
||||||
test_ecdh_generator_basepoint();
|
|
||||||
test_bad_scalar();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
*~
|
||||||
|
\#*#
|
|
@ -0,0 +1,7 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- '1.8'
|
||||||
|
- '1.9'
|
||||||
|
- '1.10'
|
||||||
|
- tip
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2014-2015 Lann Martin
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,68 @@
|
||||||
|
# Builder - fluent immutable builders for Go
|
||||||
|
|
||||||
|
[![GoDoc](https://godoc.org/github.com/lann/builder?status.png)](https://godoc.org/github.com/lann/builder)
|
||||||
|
[![Build Status](https://travis-ci.org/lann/builder.png?branch=master)](https://travis-ci.org/lann/builder)
|
||||||
|
|
||||||
|
Builder was originally written for
|
||||||
|
[Squirrel](https://github.com/lann/squirrel), a fluent SQL generator. It
|
||||||
|
is probably the best example of Builder in action.
|
||||||
|
|
||||||
|
Builder helps you write **fluent** DSLs for your libraries with method chaining:
|
||||||
|
|
||||||
|
```go
|
||||||
|
resp := ReqBuilder.
|
||||||
|
Url("http://golang.org").
|
||||||
|
Header("User-Agent", "Builder").
|
||||||
|
Get()
|
||||||
|
```
|
||||||
|
|
||||||
|
Builder uses **immutable** persistent data structures
|
||||||
|
([these](https://github.com/mndrix/ps), specifically)
|
||||||
|
so that each step in your method chain can be reused:
|
||||||
|
|
||||||
|
```go
|
||||||
|
build := WordBuilder.AddLetters("Build")
|
||||||
|
builder := build.AddLetters("er")
|
||||||
|
building := build.AddLetters("ing")
|
||||||
|
```
|
||||||
|
|
||||||
|
Builder makes it easy to **build** structs using the **builder** pattern
|
||||||
|
(*surprise!*):
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/lann/builder"
|
||||||
|
|
||||||
|
type Muppet struct {
|
||||||
|
Name string
|
||||||
|
Friends []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type muppetBuilder builder.Builder
|
||||||
|
|
||||||
|
func (b muppetBuilder) Name(name string) muppetBuilder {
|
||||||
|
return builder.Set(b, "Name", name).(muppetBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b muppetBuilder) AddFriend(friend string) muppetBuilder {
|
||||||
|
return builder.Append(b, "Friends", friend).(muppetBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b muppetBuilder) Build() Muppet {
|
||||||
|
return builder.GetStruct(b).(Muppet)
|
||||||
|
}
|
||||||
|
|
||||||
|
var MuppetBuilder = builder.Register(muppetBuilder{}, Muppet{}).(muppetBuilder)
|
||||||
|
```
|
||||||
|
```go
|
||||||
|
MuppetBuilder.
|
||||||
|
Name("Beaker").
|
||||||
|
AddFriend("Dr. Honeydew").
|
||||||
|
Build()
|
||||||
|
|
||||||
|
=> Muppet{Name:"Beaker", Friends:[]string{"Dr. Honeydew"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Builder is released under the
|
||||||
|
[MIT License](http://www.opensource.org/licenses/MIT).
|
|
@ -0,0 +1,225 @@
|
||||||
|
// Package builder provides a method for writing fluent immutable builders.
|
||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lann/ps"
|
||||||
|
"go/ast"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Builder stores a set of named values.
|
||||||
|
//
|
||||||
|
// New types can be declared with underlying type Builder and used with the
|
||||||
|
// functions in this package. See example.
|
||||||
|
//
|
||||||
|
// Instances of Builder should be treated as immutable. It is up to the
|
||||||
|
// implementor to ensure mutable values set on a Builder are not mutated while
|
||||||
|
// the Builder is in use.
|
||||||
|
type Builder struct {
|
||||||
|
builderMap ps.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
EmptyBuilder = Builder{ps.NewMap()}
|
||||||
|
emptyBuilderValue = reflect.ValueOf(EmptyBuilder)
|
||||||
|
)
|
||||||
|
|
||||||
|
func getBuilderMap(builder interface{}) ps.Map {
|
||||||
|
b := convert(builder, Builder{}).(Builder)
|
||||||
|
|
||||||
|
if b.builderMap == nil {
|
||||||
|
return ps.NewMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.builderMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set returns a copy of the given builder with a new value set for the given
|
||||||
|
// name.
|
||||||
|
//
|
||||||
|
// Set (and all other functions taking a builder in this package) will panic if
|
||||||
|
// the given builder's underlying type is not Builder.
|
||||||
|
func Set(builder interface{}, name string, v interface{}) interface{} {
|
||||||
|
b := Builder{getBuilderMap(builder).Set(name, v)}
|
||||||
|
return convert(b, builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete returns a copy of the given builder with the given named value unset.
|
||||||
|
func Delete(builder interface{}, name string) interface{} {
|
||||||
|
b := Builder{getBuilderMap(builder).Delete(name)}
|
||||||
|
return convert(b, builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append returns a copy of the given builder with new value(s) appended to the
|
||||||
|
// named list. If the value was previously unset or set with Set (even to a e.g.
|
||||||
|
// slice values), the new value(s) will be appended to an empty list.
|
||||||
|
func Append(builder interface{}, name string, vs ...interface{}) interface{} {
|
||||||
|
return Extend(builder, name, vs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend behaves like Append, except it takes a single slice or array value
|
||||||
|
// which will be concatenated to the named list.
|
||||||
|
//
|
||||||
|
// Unlike a variadic call to Append - which requires a []interface{} value -
|
||||||
|
// Extend accepts slices or arrays of any type.
|
||||||
|
//
|
||||||
|
// Extend will panic if the given value is not a slice, array, or nil.
|
||||||
|
func Extend(builder interface{}, name string, vs interface{}) interface{} {
|
||||||
|
if vs == nil {
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
|
maybeList, ok := getBuilderMap(builder).Lookup(name)
|
||||||
|
|
||||||
|
var list ps.List
|
||||||
|
if ok {
|
||||||
|
list, ok = maybeList.(ps.List)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
list = ps.NewList()
|
||||||
|
}
|
||||||
|
|
||||||
|
forEach(vs, func(v interface{}) {
|
||||||
|
list = list.Cons(v)
|
||||||
|
})
|
||||||
|
|
||||||
|
return Set(builder, name, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listToSlice(list ps.List, arrayType reflect.Type) reflect.Value {
|
||||||
|
size := list.Size()
|
||||||
|
slice := reflect.MakeSlice(arrayType, size, size)
|
||||||
|
for i := size - 1; i >= 0; i-- {
|
||||||
|
val := reflect.ValueOf(list.Head())
|
||||||
|
slice.Index(i).Set(val)
|
||||||
|
list = list.Tail()
|
||||||
|
}
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
var anyArrayType = reflect.TypeOf([]interface{}{})
|
||||||
|
|
||||||
|
// Get retrieves a single named value from the given builder.
|
||||||
|
// If the value has not been set, it returns (nil, false). Otherwise, it will
|
||||||
|
// return (value, true).
|
||||||
|
//
|
||||||
|
// If the named value was last set with Append or Extend, the returned value
|
||||||
|
// will be a slice. If the given Builder has been registered with Register or
|
||||||
|
// RegisterType and the given name is an exported field of the registered
|
||||||
|
// struct, the returned slice will have the same type as that field. Otherwise
|
||||||
|
// the slice will have type []interface{}. It will panic if the given name is a
|
||||||
|
// registered struct's exported field and the value set on the Builder is not
|
||||||
|
// assignable to the field.
|
||||||
|
func Get(builder interface{}, name string) (interface{}, bool) {
|
||||||
|
val, ok := getBuilderMap(builder).Lookup(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
list, isList := val.(ps.List)
|
||||||
|
if isList {
|
||||||
|
arrayType := anyArrayType
|
||||||
|
|
||||||
|
if ast.IsExported(name) {
|
||||||
|
structType := getBuilderStructType(reflect.TypeOf(builder))
|
||||||
|
if structType != nil {
|
||||||
|
field, ok := (*structType).FieldByName(name)
|
||||||
|
if ok {
|
||||||
|
arrayType = field.Type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val = listToSlice(list, arrayType).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
return val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMap returns a map[string]interface{} of the values set in the given
|
||||||
|
// builder.
|
||||||
|
//
|
||||||
|
// See notes on Get regarding returned slices.
|
||||||
|
func GetMap(builder interface{}) map[string]interface{} {
|
||||||
|
m := getBuilderMap(builder)
|
||||||
|
structType := getBuilderStructType(reflect.TypeOf(builder))
|
||||||
|
|
||||||
|
ret := make(map[string]interface{}, m.Size())
|
||||||
|
|
||||||
|
m.ForEach(func(name string, val ps.Any) {
|
||||||
|
list, isList := val.(ps.List)
|
||||||
|
if isList {
|
||||||
|
arrayType := anyArrayType
|
||||||
|
|
||||||
|
if structType != nil {
|
||||||
|
field, ok := (*structType).FieldByName(name)
|
||||||
|
if ok {
|
||||||
|
arrayType = field.Type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val = listToSlice(list, arrayType).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
ret[name] = val
|
||||||
|
})
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStruct builds a new struct from the given registered builder.
|
||||||
|
// It will return nil if the given builder's type has not been registered with
|
||||||
|
// Register or RegisterValue.
|
||||||
|
//
|
||||||
|
// All values set on the builder with names that start with an uppercase letter
|
||||||
|
// (i.e. which would be exported if they were identifiers) are assigned to the
|
||||||
|
// corresponding exported fields of the struct.
|
||||||
|
//
|
||||||
|
// GetStruct will panic if any of these "exported" values are not assignable to
|
||||||
|
// their corresponding struct fields.
|
||||||
|
func GetStruct(builder interface{}) interface{} {
|
||||||
|
structVal := newBuilderStruct(reflect.TypeOf(builder))
|
||||||
|
if structVal == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return scanStruct(builder, structVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStructLike builds a new struct from the given builder with the same type
|
||||||
|
// as the given struct.
|
||||||
|
//
|
||||||
|
// All values set on the builder with names that start with an uppercase letter
|
||||||
|
// (i.e. which would be exported if they were identifiers) are assigned to the
|
||||||
|
// corresponding exported fields of the struct.
|
||||||
|
//
|
||||||
|
// ScanStruct will panic if any of these "exported" values are not assignable to
|
||||||
|
// their corresponding struct fields.
|
||||||
|
func GetStructLike(builder interface{}, strct interface{}) interface{} {
|
||||||
|
structVal := reflect.New(reflect.TypeOf(strct)).Elem()
|
||||||
|
return scanStruct(builder, &structVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func scanStruct(builder interface{}, structVal *reflect.Value) interface{} {
|
||||||
|
getBuilderMap(builder).ForEach(func(name string, val ps.Any) {
|
||||||
|
if ast.IsExported(name) {
|
||||||
|
field := structVal.FieldByName(name)
|
||||||
|
|
||||||
|
var value reflect.Value
|
||||||
|
switch v := val.(type) {
|
||||||
|
case nil:
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||||
|
value = reflect.Zero(field.Type())
|
||||||
|
}
|
||||||
|
// nil is not valid for this Type; Set will panic
|
||||||
|
case ps.List:
|
||||||
|
value = listToSlice(v, field.Type())
|
||||||
|
default:
|
||||||
|
value = reflect.ValueOf(val)
|
||||||
|
}
|
||||||
|
field.Set(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return structVal.Interface()
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package builder
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
func convert(from interface{}, to interface{}) interface{} {
|
||||||
|
return reflect.
|
||||||
|
ValueOf(from).
|
||||||
|
Convert(reflect.TypeOf(to)).
|
||||||
|
Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func forEach(s interface{}, f func(interface{})) {
|
||||||
|
val := reflect.ValueOf(s)
|
||||||
|
|
||||||
|
kind := val.Kind()
|
||||||
|
if kind != reflect.Slice && kind != reflect.Array {
|
||||||
|
panic(&reflect.ValueError{Method: "builder.forEach", Kind: kind})
|
||||||
|
}
|
||||||
|
|
||||||
|
l := val.Len()
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
f(val.Index(i).Interface())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
registry = make(map[reflect.Type]reflect.Type)
|
||||||
|
registryMux sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterType maps the given builderType to a structType.
|
||||||
|
// This mapping affects the type of slices returned by Get and is required for
|
||||||
|
// GetStruct to work.
|
||||||
|
//
|
||||||
|
// Returns a Value containing an empty instance of the registered builderType.
|
||||||
|
//
|
||||||
|
// RegisterType will panic if builderType's underlying type is not Builder or
|
||||||
|
// if structType's Kind is not Struct.
|
||||||
|
func RegisterType(builderType reflect.Type, structType reflect.Type) *reflect.Value {
|
||||||
|
registryMux.Lock()
|
||||||
|
defer registryMux.Unlock()
|
||||||
|
structType.NumField() // Panic if structType is not a struct
|
||||||
|
registry[builderType] = structType
|
||||||
|
emptyValue := emptyBuilderValue.Convert(builderType)
|
||||||
|
return &emptyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register wraps RegisterType, taking instances instead of Types.
|
||||||
|
//
|
||||||
|
// Returns an empty instance of the registered builder type which can be used
|
||||||
|
// as the initial value for builder expressions. See example.
|
||||||
|
func Register(builderProto, structProto interface{}) interface{} {
|
||||||
|
empty := RegisterType(
|
||||||
|
reflect.TypeOf(builderProto),
|
||||||
|
reflect.TypeOf(structProto),
|
||||||
|
).Interface()
|
||||||
|
return empty
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBuilderStructType(builderType reflect.Type) *reflect.Type {
|
||||||
|
registryMux.RLock()
|
||||||
|
defer registryMux.RUnlock()
|
||||||
|
structType, ok := registry[builderType]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &structType
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuilderStruct(builderType reflect.Type) *reflect.Value {
|
||||||
|
structType := getBuilderStructType(builderType)
|
||||||
|
if structType == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newStruct := reflect.New(*structType).Elem()
|
||||||
|
return &newStruct
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
Copyright (c) 2013 Michael Hendricks
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,10 @@
|
||||||
|
**This is a stable fork of https://github.com/mndrix/ps; it will not introduce breaking changes.**
|
||||||
|
|
||||||
|
ps
|
||||||
|
==
|
||||||
|
|
||||||
|
Persistent data structures for Go. See the [full package documentation](http://godoc.org/github.com/lann/ps)
|
||||||
|
|
||||||
|
Install with
|
||||||
|
|
||||||
|
go get github.com/lann/ps
|
|
@ -0,0 +1,93 @@
|
||||||
|
package ps
|
||||||
|
|
||||||
|
// List is a persistent list of possibly heterogenous values.
|
||||||
|
type List interface {
|
||||||
|
// IsNil returns true if the list is empty
|
||||||
|
IsNil() bool
|
||||||
|
|
||||||
|
// Cons returns a new list with val as the head
|
||||||
|
Cons(val Any) List
|
||||||
|
|
||||||
|
// Head returns the first element of the list;
|
||||||
|
// panics if the list is empty
|
||||||
|
Head() Any
|
||||||
|
|
||||||
|
// Tail returns a list with all elements except the head;
|
||||||
|
// panics if the list is empty
|
||||||
|
Tail() List
|
||||||
|
|
||||||
|
// Size returns the list's length. This takes O(1) time.
|
||||||
|
Size() int
|
||||||
|
|
||||||
|
// ForEach executes a callback for each value in the list.
|
||||||
|
ForEach(f func(Any))
|
||||||
|
|
||||||
|
// Reverse returns a list whose elements are in the opposite order as
|
||||||
|
// the original list.
|
||||||
|
Reverse() List
|
||||||
|
}
|
||||||
|
|
||||||
|
// Immutable (i.e. persistent) list
|
||||||
|
type list struct {
|
||||||
|
depth int // the number of nodes after, and including, this one
|
||||||
|
value Any
|
||||||
|
tail *list
|
||||||
|
}
|
||||||
|
|
||||||
|
// An empty list shared by all lists
|
||||||
|
var nilList = &list{}
|
||||||
|
|
||||||
|
// NewList returns a new, empty list. The result is a singly linked
|
||||||
|
// list implementation. All lists share an empty tail, so allocating
|
||||||
|
// empty lists is efficient in time and memory.
|
||||||
|
func NewList() List {
|
||||||
|
return nilList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *list) IsNil() bool {
|
||||||
|
return self == nilList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *list) Size() int {
|
||||||
|
return self.depth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tail *list) Cons(val Any) List {
|
||||||
|
var xs list
|
||||||
|
xs.depth = tail.depth + 1
|
||||||
|
xs.value = val
|
||||||
|
xs.tail = tail
|
||||||
|
return &xs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *list) Head() Any {
|
||||||
|
if self.IsNil() {
|
||||||
|
panic("Called Head() on an empty list")
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *list) Tail() List {
|
||||||
|
if self.IsNil() {
|
||||||
|
panic("Called Tail() on an empty list")
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.tail
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForEach executes a callback for each value in the list
|
||||||
|
func (self *list) ForEach(f func(Any)) {
|
||||||
|
if self.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f(self.Head())
|
||||||
|
self.Tail().ForEach(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse returns a list with elements in opposite order as this list
|
||||||
|
func (self *list) Reverse() List {
|
||||||
|
reversed := NewList()
|
||||||
|
self.ForEach(func(v Any) { reversed = reversed.Cons(v) })
|
||||||
|
return reversed
|
||||||
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
// Fully persistent data structures. A persistent data structure is a data
|
||||||
|
// structure that always preserves the previous version of itself when
|
||||||
|
// it is modified. Such data structures are effectively immutable,
|
||||||
|
// as their operations do not update the structure in-place, but instead
|
||||||
|
// always yield a new structure.
|
||||||
|
//
|
||||||
|
// Persistent
|
||||||
|
// data structures typically share structure among themselves. This allows
|
||||||
|
// operations to avoid copying the entire data structure.
|
||||||
|
package ps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Any is a shorthand for Go's verbose interface{} type.
|
||||||
|
type Any interface{}
|
||||||
|
|
||||||
|
// A Map associates unique keys (type string) with values (type Any).
|
||||||
|
type Map interface {
|
||||||
|
// IsNil returns true if the Map is empty
|
||||||
|
IsNil() bool
|
||||||
|
|
||||||
|
// Set returns a new map in which key and value are associated.
|
||||||
|
// If the key didn't exist before, it's created; otherwise, the
|
||||||
|
// associated value is changed.
|
||||||
|
// This operation is O(log N) in the number of keys.
|
||||||
|
Set(key string, value Any) Map
|
||||||
|
|
||||||
|
// Delete returns a new map with the association for key, if any, removed.
|
||||||
|
// This operation is O(log N) in the number of keys.
|
||||||
|
Delete(key string) Map
|
||||||
|
|
||||||
|
// Lookup returns the value associated with a key, if any. If the key
|
||||||
|
// exists, the second return value is true; otherwise, false.
|
||||||
|
// This operation is O(log N) in the number of keys.
|
||||||
|
Lookup(key string) (Any, bool)
|
||||||
|
|
||||||
|
// Size returns the number of key value pairs in the map.
|
||||||
|
// This takes O(1) time.
|
||||||
|
Size() int
|
||||||
|
|
||||||
|
// ForEach executes a callback on each key value pair in the map.
|
||||||
|
ForEach(f func(key string, val Any))
|
||||||
|
|
||||||
|
// Keys returns a slice with all keys in this map.
|
||||||
|
// This operation is O(N) in the number of keys.
|
||||||
|
Keys() []string
|
||||||
|
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Immutable (i.e. persistent) associative array
|
||||||
|
const childCount = 8
|
||||||
|
const shiftSize = 3
|
||||||
|
|
||||||
|
type tree struct {
|
||||||
|
count int
|
||||||
|
hash uint64 // hash of the key (used for tree balancing)
|
||||||
|
key string
|
||||||
|
value Any
|
||||||
|
children [childCount]*tree
|
||||||
|
}
|
||||||
|
|
||||||
|
var nilMap = &tree{}
|
||||||
|
|
||||||
|
// Recursively set nilMap's subtrees to point at itself.
|
||||||
|
// This eliminates all nil pointers in the map structure.
|
||||||
|
// All map nodes are created by cloning this structure so
|
||||||
|
// they avoid the problem too.
|
||||||
|
func init() {
|
||||||
|
for i := range nilMap.children {
|
||||||
|
nilMap.children[i] = nilMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMap allocates a new, persistent map from strings to values of
|
||||||
|
// any type.
|
||||||
|
// This is currently implemented as a path-copying binary tree.
|
||||||
|
func NewMap() Map {
|
||||||
|
return nilMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *tree) IsNil() bool {
|
||||||
|
return self == nilMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone returns an exact duplicate of a tree node
|
||||||
|
func (self *tree) clone() *tree {
|
||||||
|
var m tree
|
||||||
|
m = *self
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
// constants for FNV-1a hash algorithm
|
||||||
|
const (
|
||||||
|
offset64 uint64 = 14695981039346656037
|
||||||
|
prime64 uint64 = 1099511628211
|
||||||
|
)
|
||||||
|
|
||||||
|
// hashKey returns a hash code for a given string
|
||||||
|
func hashKey(key string) uint64 {
|
||||||
|
hash := offset64
|
||||||
|
for _, codepoint := range key {
|
||||||
|
hash ^= uint64(codepoint)
|
||||||
|
hash *= prime64
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set returns a new map similar to this one but with key and value
|
||||||
|
// associated. If the key didn't exist, it's created; otherwise, the
|
||||||
|
// associated value is changed.
|
||||||
|
func (self *tree) Set(key string, value Any) Map {
|
||||||
|
hash := hashKey(key)
|
||||||
|
return setLowLevel(self, hash, hash, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setLowLevel(self *tree, partialHash, hash uint64, key string, value Any) *tree {
|
||||||
|
if self.IsNil() { // an empty tree is easy
|
||||||
|
m := self.clone()
|
||||||
|
m.count = 1
|
||||||
|
m.hash = hash
|
||||||
|
m.key = key
|
||||||
|
m.value = value
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash != self.hash {
|
||||||
|
m := self.clone()
|
||||||
|
i := partialHash % childCount
|
||||||
|
m.children[i] = setLowLevel(self.children[i], partialHash>>shiftSize, hash, key, value)
|
||||||
|
recalculateCount(m)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// replacing a key's previous value
|
||||||
|
m := self.clone()
|
||||||
|
m.value = value
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// modifies a map by recalculating its key count based on the counts
|
||||||
|
// of its subtrees
|
||||||
|
func recalculateCount(m *tree) {
|
||||||
|
count := 0
|
||||||
|
for _, t := range m.children {
|
||||||
|
count += t.Size()
|
||||||
|
}
|
||||||
|
m.count = count + 1 // add one to count ourself
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tree) Delete(key string) Map {
|
||||||
|
hash := hashKey(key)
|
||||||
|
newMap, _ := deleteLowLevel(m, hash, hash)
|
||||||
|
return newMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteLowLevel(self *tree, partialHash, hash uint64) (*tree, bool) {
|
||||||
|
// empty trees are easy
|
||||||
|
if self.IsNil() {
|
||||||
|
return self, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash != self.hash {
|
||||||
|
i := partialHash % childCount
|
||||||
|
child, found := deleteLowLevel(self.children[i], partialHash>>shiftSize, hash)
|
||||||
|
if !found {
|
||||||
|
return self, false
|
||||||
|
}
|
||||||
|
newMap := self.clone()
|
||||||
|
newMap.children[i] = child
|
||||||
|
recalculateCount(newMap)
|
||||||
|
return newMap, true // ? this wasn't in the original code
|
||||||
|
}
|
||||||
|
|
||||||
|
// we must delete our own node
|
||||||
|
if self.isLeaf() { // we have no children
|
||||||
|
return nilMap, true
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if self.subtreeCount() == 1 { // only one subtree
|
||||||
|
for _, t := range self.children {
|
||||||
|
if t != nilMap {
|
||||||
|
return t, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("Tree with 1 subtree actually had no subtrees")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// find a node to replace us
|
||||||
|
i := -1
|
||||||
|
size := -1
|
||||||
|
for j, t := range self.children {
|
||||||
|
if t.Size() > size {
|
||||||
|
i = j
|
||||||
|
size = t.Size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make chosen leaf smaller
|
||||||
|
replacement, child := self.children[i].deleteLeftmost()
|
||||||
|
newMap := replacement.clone()
|
||||||
|
for j := range self.children {
|
||||||
|
if j == i {
|
||||||
|
newMap.children[j] = child
|
||||||
|
} else {
|
||||||
|
newMap.children[j] = self.children[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recalculateCount(newMap)
|
||||||
|
return newMap, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the leftmost node in a tree returning the node that
|
||||||
|
// was deleted and the tree left over after its deletion
|
||||||
|
func (m *tree) deleteLeftmost() (*tree, *tree) {
|
||||||
|
if m.isLeaf() {
|
||||||
|
return m, nilMap
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, t := range m.children {
|
||||||
|
if t != nilMap {
|
||||||
|
deleted, child := t.deleteLeftmost()
|
||||||
|
newMap := m.clone()
|
||||||
|
newMap.children[i] = child
|
||||||
|
recalculateCount(newMap)
|
||||||
|
return deleted, newMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("Tree isn't a leaf but also had no children. How does that happen?")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isLeaf returns true if this is a leaf node
|
||||||
|
func (m *tree) isLeaf() bool {
|
||||||
|
return m.Size() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the number of child subtrees we have
|
||||||
|
func (m *tree) subtreeCount() int {
|
||||||
|
count := 0
|
||||||
|
for _, t := range m.children {
|
||||||
|
if t != nilMap {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tree) Lookup(key string) (Any, bool) {
|
||||||
|
hash := hashKey(key)
|
||||||
|
return lookupLowLevel(m, hash, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupLowLevel(self *tree, partialHash, hash uint64) (Any, bool) {
|
||||||
|
if self.IsNil() { // an empty tree is easy
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash != self.hash {
|
||||||
|
i := partialHash % childCount
|
||||||
|
return lookupLowLevel(self.children[i], partialHash>>shiftSize, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we found it
|
||||||
|
return self.value, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tree) Size() int {
|
||||||
|
return m.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tree) ForEach(f func(key string, val Any)) {
|
||||||
|
if m.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ourself
|
||||||
|
f(m.key, m.value)
|
||||||
|
|
||||||
|
// children
|
||||||
|
for _, t := range m.children {
|
||||||
|
if t != nilMap {
|
||||||
|
t.ForEach(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *tree) Keys() []string {
|
||||||
|
keys := make([]string, m.Size())
|
||||||
|
i := 0
|
||||||
|
m.ForEach(func(k string, v Any) {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
})
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// make it easier to display maps for debugging
|
||||||
|
func (m *tree) String() string {
|
||||||
|
keys := m.Keys()
|
||||||
|
buf := bytes.NewBufferString("{")
|
||||||
|
for _, key := range keys {
|
||||||
|
val, _ := m.Lookup(key)
|
||||||
|
fmt.Fprintf(buf, "%s: %s, ", key, val)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, "}\n")
|
||||||
|
return buf.String()
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
go test -c
|
||||||
|
./ps.test -test.run=none -test.bench=$2 -test.$1profile=$1.profile
|
35
vendor/github.com/mutecomm/go-sqlcipher/v4/_example/mod_regexp/sqlite3_mod_regexp.c
generated
vendored
35
vendor/github.com/mutecomm/go-sqlcipher/v4/_example/mod_regexp/sqlite3_mod_regexp.c
generated
vendored
|
@ -1,35 +0,0 @@
|
||||||
#include <pcre.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sqlite3ext.h>
|
|
||||||
|
|
||||||
SQLITE_EXTENSION_INIT1
|
|
||||||
static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
|
||||||
if (argc >= 2) {
|
|
||||||
const char *target = (const char *)sqlite3_value_text(argv[1]);
|
|
||||||
const char *pattern = (const char *)sqlite3_value_text(argv[0]);
|
|
||||||
const char* errstr = NULL;
|
|
||||||
int erroff = 0;
|
|
||||||
int vec[500];
|
|
||||||
int n, rc;
|
|
||||||
pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL);
|
|
||||||
if (!re) {
|
|
||||||
sqlite3_result_error(context, errstr, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500);
|
|
||||||
if (rc <= 0) {
|
|
||||||
sqlite3_result_int(context, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sqlite3_result_int(context, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
__declspec(dllexport)
|
|
||||||
#endif
|
|
||||||
int sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) {
|
|
||||||
SQLITE_EXTENSION_INIT2(api);
|
|
||||||
return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, (void*)db, regexp_func, NULL, NULL);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,122 +0,0 @@
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
||||||
*
|
|
||||||
* Copyright 2013 Garrett D'Amore <garrett@damore.org>
|
|
||||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
|
||||||
* Copyright (c) 2004 Tim J. Robbins.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011 The FreeBSD Foundation
|
|
||||||
* All rights reserved.
|
|
||||||
* Portions of this software were developed by David Chisnall
|
|
||||||
* under sponsorship from the FreeBSD Foundation.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. 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.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
|
||||||
*
|
|
||||||
* $FreeBSD$
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _MBLOCAL_H_
|
|
||||||
#define _MBLOCAL_H_
|
|
||||||
|
|
||||||
#include <runetype.h>
|
|
||||||
#include "xlocale_private.h"
|
|
||||||
|
|
||||||
#define SS2 0x008e
|
|
||||||
#define SS3 0x008f
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Conversion function pointers for current encoding.
|
|
||||||
*/
|
|
||||||
struct xlocale_ctype {
|
|
||||||
struct xlocale_component header;
|
|
||||||
_RuneLocale *runes;
|
|
||||||
size_t (*__mbrtowc)(wchar_t * __restrict, const char * __restrict,
|
|
||||||
size_t, mbstate_t * __restrict);
|
|
||||||
int (*__mbsinit)(const mbstate_t *);
|
|
||||||
size_t (*__mbsnrtowcs)(wchar_t * __restrict, const char ** __restrict,
|
|
||||||
size_t, size_t, mbstate_t * __restrict);
|
|
||||||
size_t (*__wcrtomb)(char * __restrict, wchar_t, mbstate_t * __restrict);
|
|
||||||
size_t (*__wcsnrtombs)(char * __restrict, const wchar_t ** __restrict,
|
|
||||||
size_t, size_t, mbstate_t * __restrict);
|
|
||||||
int __mb_cur_max;
|
|
||||||
int __mb_sb_limit;
|
|
||||||
/** Persistent state used by mblen() calls. */
|
|
||||||
__mbstate_t mblen;
|
|
||||||
/** Persistent state used by mbrlen() calls. */
|
|
||||||
__mbstate_t mbrlen;
|
|
||||||
/** Persistent state used by mbrtoc16() calls. */
|
|
||||||
__mbstate_t mbrtoc16;
|
|
||||||
/** Persistent state used by mbrtoc32() calls. */
|
|
||||||
__mbstate_t mbrtoc32;
|
|
||||||
/** Persistent state used by mbrtowc() calls. */
|
|
||||||
__mbstate_t mbrtowc;
|
|
||||||
/** Persistent state used by mbsnrtowcs() calls. */
|
|
||||||
__mbstate_t mbsnrtowcs;
|
|
||||||
/** Persistent state used by mbsrtowcs() calls. */
|
|
||||||
__mbstate_t mbsrtowcs;
|
|
||||||
/** Persistent state used by mbtowc() calls. */
|
|
||||||
__mbstate_t mbtowc;
|
|
||||||
/** Persistent state used by c16rtomb() calls. */
|
|
||||||
__mbstate_t c16rtomb;
|
|
||||||
/** Persistent state used by c32rtomb() calls. */
|
|
||||||
__mbstate_t c32rtomb;
|
|
||||||
/** Persistent state used by wcrtomb() calls. */
|
|
||||||
__mbstate_t wcrtomb;
|
|
||||||
/** Persistent state used by wcsnrtombs() calls. */
|
|
||||||
__mbstate_t wcsnrtombs;
|
|
||||||
/** Persistent state used by wcsrtombs() calls. */
|
|
||||||
__mbstate_t wcsrtombs;
|
|
||||||
/** Persistent state used by wctomb() calls. */
|
|
||||||
__mbstate_t wctomb;
|
|
||||||
};
|
|
||||||
#define XLOCALE_CTYPE(x) ((struct xlocale_ctype*)(x)->components[XLC_CTYPE])
|
|
||||||
extern struct xlocale_ctype __xlocale_global_ctype;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Rune initialization function prototypes.
|
|
||||||
*/
|
|
||||||
int _none_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _UTF8_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _EUC_CN_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _EUC_JP_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _EUC_KR_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _EUC_TW_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _GB18030_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _GB2312_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _GBK_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _BIG5_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _MSKanji_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
int _ascii_init(struct xlocale_ctype *, _RuneLocale *) __hidden;
|
|
||||||
|
|
||||||
typedef size_t (*mbrtowc_pfn_t)(wchar_t * __restrict,
|
|
||||||
const char * __restrict, size_t, mbstate_t * __restrict);
|
|
||||||
typedef size_t (*wcrtomb_pfn_t)(char * __restrict, wchar_t,
|
|
||||||
mbstate_t * __restrict);
|
|
||||||
size_t __mbsnrtowcs_std(wchar_t * __restrict, const char ** __restrict,
|
|
||||||
size_t, size_t, mbstate_t * __restrict, mbrtowc_pfn_t);
|
|
||||||
size_t __wcsnrtombs_std(char * __restrict, const wchar_t ** __restrict,
|
|
||||||
size_t, size_t, mbstate_t * __restrict, wcrtomb_pfn_t);
|
|
||||||
|
|
||||||
#endif /* _MBLOCAL_H_ */
|
|
|
@ -1,13 +0,0 @@
|
||||||
|
|
||||||
#ifndef _SETLOCALE_H_
|
|
||||||
#define _SETLOCALE_H_
|
|
||||||
|
|
||||||
#define ENCODING_LEN 31
|
|
||||||
#define CATEGORY_LEN 11
|
|
||||||
|
|
||||||
extern char *_PathLocale;
|
|
||||||
|
|
||||||
int __detect_path_locale(void);
|
|
||||||
int __wrap_setrunelocale(const char *);
|
|
||||||
|
|
||||||
#endif /* !_SETLOCALE_H_ */
|
|
|
@ -1,267 +0,0 @@
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
*
|
|
||||||
* Copyright (c) 1993
|
|
||||||
* The Regents of the University of California. All rights reserved.
|
|
||||||
*
|
|
||||||
* This code is derived from software contributed to Berkeley by
|
|
||||||
* Paul Borman at Krystal Technologies.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011 The FreeBSD Foundation
|
|
||||||
* All rights reserved.
|
|
||||||
* Portions of this software were developed by David Chisnall
|
|
||||||
* under sponsorship from the FreeBSD Foundation.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. 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.
|
|
||||||
* 3. Neither the name of the University 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 REGENTS 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 REGENTS 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(LIBC_SCCS) && !defined(lint)
|
|
||||||
static char sccsid[] = "@(#)table.c 8.1 (Berkeley) 6/27/93";
|
|
||||||
#endif /* LIBC_SCCS and not lint */
|
|
||||||
#include <sys/cdefs.h>
|
|
||||||
__FBSDID("$FreeBSD$");
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <runetype.h>
|
|
||||||
#include <wchar.h>
|
|
||||||
#include "mblocal.h"
|
|
||||||
|
|
||||||
const _RuneLocale _DefaultRuneLocale = {
|
|
||||||
_RUNE_MAGIC_1,
|
|
||||||
"NONE",
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
0xFFFD,
|
|
||||||
|
|
||||||
{ /*00*/ _CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
/*08*/ _CTYPE_C,
|
|
||||||
_CTYPE_C|_CTYPE_S|_CTYPE_B,
|
|
||||||
_CTYPE_C|_CTYPE_S,
|
|
||||||
_CTYPE_C|_CTYPE_S,
|
|
||||||
_CTYPE_C|_CTYPE_S,
|
|
||||||
_CTYPE_C|_CTYPE_S,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
/*10*/ _CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
/*18*/ _CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
_CTYPE_C,
|
|
||||||
/*20*/ _CTYPE_S|_CTYPE_B|_CTYPE_R,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
/*28*/ _CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
/*30*/ _CTYPE_D|_CTYPE_R|_CTYPE_G|_CTYPE_X|_CTYPE_N|0,
|
|
||||||
_CTYPE_D|_CTYPE_R|_CTYPE_G|_CTYPE_X|_CTYPE_N|1,
|
|
||||||
_CTYPE_D|_CTYPE_R|_CTYPE_G|_CTYPE_X|_CTYPE_N|2,
|
|
||||||
_CTYPE_D|_CTYPE_R|_CTYPE_G|_CTYPE_X|_CTYPE_N|3,
|
|
||||||
_CTYPE_D|_CTYPE_R|_CTYPE_G|_CTYPE_X|_CTYPE_N|4,
|
|
||||||
_CTYPE_D|_CTYPE_R|_CTYPE_G|_CTYPE_X|_CTYPE_N|5,
|
|
||||||
_CTYPE_D|_CTYPE_R|_CTYPE_G|_CTYPE_X|_CTYPE_N|6,
|
|
||||||
_CTYPE_D|_CTYPE_R|_CTYPE_G|_CTYPE_X|_CTYPE_N|7,
|
|
||||||
/*38*/ _CTYPE_D|_CTYPE_R|_CTYPE_G|_CTYPE_X|_CTYPE_N|8,
|
|
||||||
_CTYPE_D|_CTYPE_R|_CTYPE_G|_CTYPE_X|_CTYPE_N|9,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
/*40*/ _CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_U|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|10,
|
|
||||||
_CTYPE_U|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|11,
|
|
||||||
_CTYPE_U|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|12,
|
|
||||||
_CTYPE_U|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|13,
|
|
||||||
_CTYPE_U|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|14,
|
|
||||||
_CTYPE_U|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|15,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
/*48*/ _CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
/*50*/ _CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
/*58*/ _CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_U|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
/*60*/ _CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_L|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|10,
|
|
||||||
_CTYPE_L|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|11,
|
|
||||||
_CTYPE_L|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|12,
|
|
||||||
_CTYPE_L|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|13,
|
|
||||||
_CTYPE_L|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|14,
|
|
||||||
_CTYPE_L|_CTYPE_X|_CTYPE_R|_CTYPE_G|_CTYPE_A|15,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
/*68*/ _CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
/*70*/ _CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
/*78*/ _CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_L|_CTYPE_R|_CTYPE_G|_CTYPE_A,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_P|_CTYPE_R|_CTYPE_G,
|
|
||||||
_CTYPE_C,
|
|
||||||
},
|
|
||||||
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
||||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
||||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
||||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
||||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
|
||||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
|
||||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
|
||||||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
|
||||||
0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
|
||||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
||||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
|
||||||
'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
|
||||||
0x60, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
|
||||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
||||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
|
||||||
'x', 'y', 'z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
|
||||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
||||||
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
||||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
|
||||||
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
|
|
||||||
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
|
||||||
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
|
||||||
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
|
|
||||||
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
|
|
||||||
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
|
|
||||||
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
|
|
||||||
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
|
|
||||||
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
|
|
||||||
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
|
|
||||||
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
|
||||||
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
|
|
||||||
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
|
|
||||||
},
|
|
||||||
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
||||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
||||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
||||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
||||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
|
||||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
|
||||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
|
||||||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
|
||||||
0x40, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
||||||
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
||||||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
||||||
'X', 'Y', 'Z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
|
||||||
0x60, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
||||||
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
||||||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
||||||
'X', 'Y', 'Z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
|
||||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
||||||
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
||||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
|
||||||
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
|
|
||||||
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
|
||||||
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
|
||||||
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
|
|
||||||
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
|
|
||||||
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
|
|
||||||
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
|
|
||||||
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
|
|
||||||
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
|
|
||||||
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
|
|
||||||
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
|
||||||
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
|
|
||||||
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#undef _CurrentRuneLocale
|
|
||||||
const _RuneLocale *_CurrentRuneLocale = &_DefaultRuneLocale;
|
|
||||||
|
|
||||||
_RuneLocale *
|
|
||||||
__runes_for_locale(locale_t locale, int *mb_sb_limit)
|
|
||||||
{
|
|
||||||
FIX_LOCALE(locale);
|
|
||||||
struct xlocale_ctype *c = XLOCALE_CTYPE(locale);
|
|
||||||
*mb_sb_limit = c->__mb_sb_limit;
|
|
||||||
return c->runes;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,228 +0,0 @@
|
||||||
/*-
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011 The FreeBSD Foundation
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This software was developed by David Chisnall under sponsorship from
|
|
||||||
* the FreeBSD Foundation.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. 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.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
|
||||||
*
|
|
||||||
* $FreeBSD$
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _XLOCALE_PRIVATE__H_
|
|
||||||
#define _XLOCALE_PRIVATE__H_
|
|
||||||
|
|
||||||
#include <xlocale.h>
|
|
||||||
#include <locale.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <machine/atomic.h>
|
|
||||||
#include "setlocale.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The XLC_ values are indexes into the components array. They are defined in
|
|
||||||
* the same order as the LC_ values in locale.h, but without the LC_ALL zero
|
|
||||||
* value. Translating from LC_X to XLC_X is done by subtracting one.
|
|
||||||
*
|
|
||||||
* Any reordering of this enum should ensure that these invariants are not
|
|
||||||
* violated.
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
XLC_COLLATE = 0,
|
|
||||||
XLC_CTYPE,
|
|
||||||
XLC_MONETARY,
|
|
||||||
XLC_NUMERIC,
|
|
||||||
XLC_TIME,
|
|
||||||
XLC_MESSAGES,
|
|
||||||
XLC_LAST
|
|
||||||
};
|
|
||||||
|
|
||||||
_Static_assert(XLC_LAST - XLC_COLLATE == 6, "XLC values should be contiguous");
|
|
||||||
_Static_assert(XLC_COLLATE == LC_COLLATE - 1,
|
|
||||||
"XLC_COLLATE doesn't match the LC_COLLATE value.");
|
|
||||||
_Static_assert(XLC_CTYPE == LC_CTYPE - 1,
|
|
||||||
"XLC_CTYPE doesn't match the LC_CTYPE value.");
|
|
||||||
_Static_assert(XLC_MONETARY == LC_MONETARY - 1,
|
|
||||||
"XLC_MONETARY doesn't match the LC_MONETARY value.");
|
|
||||||
_Static_assert(XLC_NUMERIC == LC_NUMERIC - 1,
|
|
||||||
"XLC_NUMERIC doesn't match the LC_NUMERIC value.");
|
|
||||||
_Static_assert(XLC_TIME == LC_TIME - 1,
|
|
||||||
"XLC_TIME doesn't match the LC_TIME value.");
|
|
||||||
_Static_assert(XLC_MESSAGES == LC_MESSAGES - 1,
|
|
||||||
"XLC_MESSAGES doesn't match the LC_MESSAGES value.");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Header used for objects that are reference counted. Objects may optionally
|
|
||||||
* have a destructor associated, which is responsible for destroying the
|
|
||||||
* structure. Global / static versions of the structure should have no
|
|
||||||
* destructor set - they can then have their reference counts manipulated as
|
|
||||||
* normal, but will not do anything with them.
|
|
||||||
*
|
|
||||||
* The header stores a retain count - objects are assumed to have a reference
|
|
||||||
* count of 1 when they are created, but the retain count is 0. When the
|
|
||||||
* retain count is less than 0, they are freed.
|
|
||||||
*/
|
|
||||||
struct xlocale_refcounted {
|
|
||||||
/** Number of references to this component. */
|
|
||||||
long retain_count;
|
|
||||||
/** Function used to destroy this component, if one is required*/
|
|
||||||
void(*destructor)(void*);
|
|
||||||
};
|
|
||||||
|
|
||||||
#define XLOCALE_DEF_VERSION_LEN 12
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Header for a locale component. All locale components must begin with this
|
|
||||||
* header.
|
|
||||||
*/
|
|
||||||
struct xlocale_component {
|
|
||||||
struct xlocale_refcounted header;
|
|
||||||
/** Name of the locale used for this component. */
|
|
||||||
char locale[ENCODING_LEN+1];
|
|
||||||
/** Version of the definition for this component. */
|
|
||||||
char version[XLOCALE_DEF_VERSION_LEN];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xlocale structure, stores per-thread locale information.
|
|
||||||
*/
|
|
||||||
struct _xlocale {
|
|
||||||
struct xlocale_refcounted header;
|
|
||||||
/** Components for the locale. */
|
|
||||||
struct xlocale_component *components[XLC_LAST];
|
|
||||||
/** Flag indicating if components[XLC_MONETARY] has changed since the
|
|
||||||
* last call to localeconv_l() with this locale. */
|
|
||||||
int monetary_locale_changed;
|
|
||||||
/** Flag indicating whether this locale is actually using a locale for
|
|
||||||
* LC_MONETARY (1), or if it should use the C default instead (0). */
|
|
||||||
int using_monetary_locale;
|
|
||||||
/** Flag indicating if components[XLC_NUMERIC] has changed since the
|
|
||||||
* last call to localeconv_l() with this locale. */
|
|
||||||
int numeric_locale_changed;
|
|
||||||
/** Flag indicating whether this locale is actually using a locale for
|
|
||||||
* LC_NUMERIC (1), or if it should use the C default instead (0). */
|
|
||||||
int using_numeric_locale;
|
|
||||||
/** Flag indicating whether this locale is actually using a locale for
|
|
||||||
* LC_TIME (1), or if it should use the C default instead (0). */
|
|
||||||
int using_time_locale;
|
|
||||||
/** Flag indicating whether this locale is actually using a locale for
|
|
||||||
* LC_MESSAGES (1), or if it should use the C default instead (0). */
|
|
||||||
int using_messages_locale;
|
|
||||||
/** The structure to be returned from localeconv_l() for this locale. */
|
|
||||||
struct lconv lconv;
|
|
||||||
/** Buffer used by nl_langinfo_l() */
|
|
||||||
char *csym;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increments the reference count of a reference-counted structure.
|
|
||||||
*/
|
|
||||||
__attribute__((unused)) static void*
|
|
||||||
xlocale_retain(void *val)
|
|
||||||
{
|
|
||||||
struct xlocale_refcounted *obj = val;
|
|
||||||
atomic_add_long(&(obj->retain_count), 1);
|
|
||||||
return (val);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Decrements the reference count of a reference-counted structure, freeing it
|
|
||||||
* if this is the last reference, calling its destructor if it has one.
|
|
||||||
*/
|
|
||||||
__attribute__((unused)) static void
|
|
||||||
xlocale_release(void *val)
|
|
||||||
{
|
|
||||||
struct xlocale_refcounted *obj = val;
|
|
||||||
long count;
|
|
||||||
|
|
||||||
count = atomic_fetchadd_long(&(obj->retain_count), -1) - 1;
|
|
||||||
if (count < 0 && obj->destructor != NULL)
|
|
||||||
obj->destructor(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load functions. Each takes the name of a locale and a pointer to the data
|
|
||||||
* to be initialised as arguments. Two special values are allowed for the
|
|
||||||
*/
|
|
||||||
extern void* __collate_load(const char*, locale_t);
|
|
||||||
extern void* __ctype_load(const char*, locale_t);
|
|
||||||
extern void* __messages_load(const char*, locale_t);
|
|
||||||
extern void* __monetary_load(const char*, locale_t);
|
|
||||||
extern void* __numeric_load(const char*, locale_t);
|
|
||||||
extern void* __time_load(const char*, locale_t);
|
|
||||||
|
|
||||||
extern struct _xlocale __xlocale_global_locale;
|
|
||||||
extern struct _xlocale __xlocale_C_locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Caches the rune table in TLS for fast access.
|
|
||||||
*/
|
|
||||||
void __set_thread_rune_locale(locale_t loc);
|
|
||||||
/**
|
|
||||||
* Flag indicating whether a per-thread locale has been set. If no per-thread
|
|
||||||
* locale has ever been set, then we always use the global locale.
|
|
||||||
*/
|
|
||||||
extern int __has_thread_locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The per-thread locale. Avoids the need to use pthread lookup functions when
|
|
||||||
* getting the per-thread locale.
|
|
||||||
*/
|
|
||||||
extern _Thread_local locale_t __thread_locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current locale for this thread, or the global locale if none is
|
|
||||||
* set. The caller does not have to free the locale. The return value from
|
|
||||||
* this call is not guaranteed to remain valid after the locale changes. As
|
|
||||||
* such, this should only be called within libc functions.
|
|
||||||
*/
|
|
||||||
static inline locale_t __get_locale(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (!__has_thread_locale) {
|
|
||||||
return (&__xlocale_global_locale);
|
|
||||||
}
|
|
||||||
return (__thread_locale ? __thread_locale : &__xlocale_global_locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Two magic values are allowed for locale_t objects. NULL and -1. This
|
|
||||||
* function maps those to the real locales that they represent.
|
|
||||||
*/
|
|
||||||
static inline locale_t get_real_locale(locale_t locale)
|
|
||||||
{
|
|
||||||
switch ((intptr_t)locale) {
|
|
||||||
case 0: return (&__xlocale_C_locale);
|
|
||||||
case -1: return (&__xlocale_global_locale);
|
|
||||||
default: return (locale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace a placeholder locale with the real global or thread-local locale_t.
|
|
||||||
*/
|
|
||||||
#define FIX_LOCALE(l) (l = get_real_locale(l))
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,82 +0,0 @@
|
||||||
#define a_ll a_ll
|
|
||||||
static inline int a_ll(volatile int *p)
|
|
||||||
{
|
|
||||||
int v;
|
|
||||||
__asm__ __volatile__ ("ldaxr %w0,%1" : "=r"(v) : "Q"(*p));
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define a_sc a_sc
|
|
||||||
static inline int a_sc(volatile int *p, int v)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
__asm__ __volatile__ ("stlxr %w0,%w2,%1" : "=&r"(r), "=Q"(*p) : "r"(v) : "memory");
|
|
||||||
return !r;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define a_barrier a_barrier
|
|
||||||
static inline void a_barrier()
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("dmb ish" : : : "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
#define a_cas a_cas
|
|
||||||
static inline int a_cas(volatile int *p, int t, int s)
|
|
||||||
{
|
|
||||||
int old;
|
|
||||||
do {
|
|
||||||
old = a_ll(p);
|
|
||||||
if (old != t) {
|
|
||||||
a_barrier();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (!a_sc(p, s));
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define a_ll_p a_ll_p
|
|
||||||
static inline void *a_ll_p(volatile void *p)
|
|
||||||
{
|
|
||||||
void *v;
|
|
||||||
__asm__ __volatile__ ("ldaxr %0, %1" : "=r"(v) : "Q"(*(void *volatile *)p));
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define a_sc_p a_sc_p
|
|
||||||
static inline int a_sc_p(volatile int *p, void *v)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
__asm__ __volatile__ ("stlxr %w0,%2,%1" : "=&r"(r), "=Q"(*(void *volatile *)p) : "r"(v) : "memory");
|
|
||||||
return !r;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define a_cas_p a_cas_p
|
|
||||||
static inline void *a_cas_p(volatile void *p, void *t, void *s)
|
|
||||||
{
|
|
||||||
void *old;
|
|
||||||
do {
|
|
||||||
old = a_ll_p(p);
|
|
||||||
if (old != t) {
|
|
||||||
a_barrier();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (!a_sc_p(p, s));
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define a_ctz_64 a_ctz_64
|
|
||||||
static inline int a_ctz_64(uint64_t x)
|
|
||||||
{
|
|
||||||
__asm__(
|
|
||||||
" rbit %0, %1\n"
|
|
||||||
" clz %0, %0\n"
|
|
||||||
: "=r"(x) : "r"(x));
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define a_clz_64 a_clz_64
|
|
||||||
static inline int a_clz_64(uint64_t x)
|
|
||||||
{
|
|
||||||
__asm__("clz %0, %1" : "=r"(x) : "r"(x));
|
|
||||||
return x;
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
#define O_CREAT 0100
|
|
||||||
#define O_EXCL 0200
|
|
||||||
#define O_NOCTTY 0400
|
|
||||||
#define O_TRUNC 01000
|
|
||||||
#define O_APPEND 02000
|
|
||||||
#define O_NONBLOCK 04000
|
|
||||||
#define O_DSYNC 010000
|
|
||||||
#define O_SYNC 04010000
|
|
||||||
#define O_RSYNC 04010000
|
|
||||||
#define O_DIRECTORY 040000
|
|
||||||
#define O_NOFOLLOW 0100000
|
|
||||||
#define O_CLOEXEC 02000000
|
|
||||||
|
|
||||||
#define O_ASYNC 020000
|
|
||||||
#define O_DIRECT 0200000
|
|
||||||
#define O_LARGEFILE 0400000
|
|
||||||
#define O_NOATIME 01000000
|
|
||||||
#define O_PATH 010000000
|
|
||||||
#define O_TMPFILE 020040000
|
|
||||||
#define O_NDELAY O_NONBLOCK
|
|
||||||
|
|
||||||
#define F_DUPFD 0
|
|
||||||
#define F_GETFD 1
|
|
||||||
#define F_SETFD 2
|
|
||||||
#define F_GETFL 3
|
|
||||||
#define F_SETFL 4
|
|
||||||
#define F_GETLK 5
|
|
||||||
#define F_SETLK 6
|
|
||||||
#define F_SETLKW 7
|
|
||||||
#define F_SETOWN 8
|
|
||||||
#define F_GETOWN 9
|
|
||||||
#define F_SETSIG 10
|
|
||||||
#define F_GETSIG 11
|
|
||||||
|
|
||||||
#define F_SETOWN_EX 15
|
|
||||||
#define F_GETOWN_EX 16
|
|
||||||
|
|
||||||
#define F_GETOWNER_UIDS 17
|
|
|
@ -1,19 +0,0 @@
|
||||||
#define FE_INVALID 1
|
|
||||||
#define FE_DIVBYZERO 2
|
|
||||||
#define FE_OVERFLOW 4
|
|
||||||
#define FE_UNDERFLOW 8
|
|
||||||
#define FE_INEXACT 16
|
|
||||||
#define FE_ALL_EXCEPT 31
|
|
||||||
#define FE_TONEAREST 0
|
|
||||||
#define FE_DOWNWARD 0x800000
|
|
||||||
#define FE_UPWARD 0x400000
|
|
||||||
#define FE_TOWARDZERO 0xc00000
|
|
||||||
|
|
||||||
typedef unsigned int fexcept_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned int __fpcr;
|
|
||||||
unsigned int __fpsr;
|
|
||||||
} fenv_t;
|
|
||||||
|
|
||||||
#define FE_DFL_ENV ((const fenv_t *) -1)
|
|
|
@ -1,31 +0,0 @@
|
||||||
#define FLT_EVAL_METHOD 0
|
|
||||||
|
|
||||||
// #define LDBL_TRUE_MIN 6.47517511943802511092443895822764655e-4966L
|
|
||||||
// #define LDBL_MIN 3.36210314311209350626267781732175260e-4932L
|
|
||||||
// #define LDBL_MAX 1.18973149535723176508575932662800702e+4932L
|
|
||||||
// #define LDBL_EPSILON 1.92592994438723585305597794258492732e-34L
|
|
||||||
//
|
|
||||||
// #define LDBL_MANT_DIG 113
|
|
||||||
// #define LDBL_MIN_EXP (-16381)
|
|
||||||
// #define LDBL_MAX_EXP 16384
|
|
||||||
//
|
|
||||||
// #define LDBL_DIG 33
|
|
||||||
// #define LDBL_MIN_10_EXP (-4931)
|
|
||||||
// #define LDBL_MAX_10_EXP 4932
|
|
||||||
//
|
|
||||||
// #define DECIMAL_DIG 36
|
|
||||||
|
|
||||||
#define LDBL_TRUE_MIN 4.94065645841246544177e-324L
|
|
||||||
#define LDBL_MIN 2.22507385850720138309e-308L
|
|
||||||
#define LDBL_MAX 1.79769313486231570815e+308L
|
|
||||||
#define LDBL_EPSILON 2.22044604925031308085e-16L
|
|
||||||
|
|
||||||
#define LDBL_MANT_DIG 53
|
|
||||||
#define LDBL_MIN_EXP (-1021)
|
|
||||||
#define LDBL_MAX_EXP 1024
|
|
||||||
|
|
||||||
#define LDBL_DIG 15
|
|
||||||
#define LDBL_MIN_10_EXP (-307)
|
|
||||||
#define LDBL_MAX_10_EXP 308
|
|
||||||
|
|
||||||
#define DECIMAL_DIG 17
|
|
|
@ -1,40 +0,0 @@
|
||||||
#define HWCAP_FP (1 << 0)
|
|
||||||
#define HWCAP_ASIMD (1 << 1)
|
|
||||||
#define HWCAP_EVTSTRM (1 << 2)
|
|
||||||
#define HWCAP_AES (1 << 3)
|
|
||||||
#define HWCAP_PMULL (1 << 4)
|
|
||||||
#define HWCAP_SHA1 (1 << 5)
|
|
||||||
#define HWCAP_SHA2 (1 << 6)
|
|
||||||
#define HWCAP_CRC32 (1 << 7)
|
|
||||||
#define HWCAP_ATOMICS (1 << 8)
|
|
||||||
#define HWCAP_FPHP (1 << 9)
|
|
||||||
#define HWCAP_ASIMDHP (1 << 10)
|
|
||||||
#define HWCAP_CPUID (1 << 11)
|
|
||||||
#define HWCAP_ASIMDRDM (1 << 12)
|
|
||||||
#define HWCAP_JSCVT (1 << 13)
|
|
||||||
#define HWCAP_FCMA (1 << 14)
|
|
||||||
#define HWCAP_LRCPC (1 << 15)
|
|
||||||
#define HWCAP_DCPOP (1 << 16)
|
|
||||||
#define HWCAP_SHA3 (1 << 17)
|
|
||||||
#define HWCAP_SM3 (1 << 18)
|
|
||||||
#define HWCAP_SM4 (1 << 19)
|
|
||||||
#define HWCAP_ASIMDDP (1 << 20)
|
|
||||||
#define HWCAP_SHA512 (1 << 21)
|
|
||||||
#define HWCAP_SVE (1 << 22)
|
|
||||||
#define HWCAP_ASIMDFHM (1 << 23)
|
|
||||||
#define HWCAP_DIT (1 << 24)
|
|
||||||
#define HWCAP_USCAT (1 << 25)
|
|
||||||
#define HWCAP_ILRCPC (1 << 26)
|
|
||||||
#define HWCAP_FLAGM (1 << 27)
|
|
||||||
#define HWCAP_SSBS (1 << 28)
|
|
||||||
#define HWCAP_SB (1 << 29)
|
|
||||||
#define HWCAP_PACA (1 << 30)
|
|
||||||
#define HWCAP_PACG (1UL << 31)
|
|
||||||
|
|
||||||
#define HWCAP2_DCPODP (1 << 0)
|
|
||||||
#define HWCAP2_SVE2 (1 << 1)
|
|
||||||
#define HWCAP2_SVEAES (1 << 2)
|
|
||||||
#define HWCAP2_SVEPMULL (1 << 3)
|
|
||||||
#define HWCAP2_SVEBITPERM (1 << 4)
|
|
||||||
#define HWCAP2_SVESHA3 (1 << 5)
|
|
||||||
#define HWCAP2_SVESM4 (1 << 6)
|
|
|
@ -1,2 +0,0 @@
|
||||||
#define _POSIX_V6_LP64_OFF64 1
|
|
||||||
#define _POSIX_V7_LP64_OFF64 1
|
|
|
@ -1,2 +0,0 @@
|
||||||
#undef __WORDSIZE
|
|
||||||
#define __WORDSIZE 64
|
|
|
@ -1 +0,0 @@
|
||||||
typedef unsigned long __jmp_buf[22];
|
|
|
@ -1,153 +0,0 @@
|
||||||
#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
|
|
||||||
|| defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
|
||||||
|
|
||||||
#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
|
||||||
#define MINSIGSTKSZ 6144
|
|
||||||
#define SIGSTKSZ 12288
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
|
||||||
typedef unsigned long greg_t;
|
|
||||||
typedef unsigned long gregset_t[34];
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
long double vregs[32];
|
|
||||||
unsigned int fpsr;
|
|
||||||
unsigned int fpcr;
|
|
||||||
} fpregset_t;
|
|
||||||
typedef struct sigcontext {
|
|
||||||
unsigned long fault_address;
|
|
||||||
unsigned long regs[31];
|
|
||||||
unsigned long sp, pc, pstate;
|
|
||||||
long double __reserved[256];
|
|
||||||
} mcontext_t;
|
|
||||||
|
|
||||||
#define FPSIMD_MAGIC 0x46508001
|
|
||||||
#define ESR_MAGIC 0x45535201
|
|
||||||
#define EXTRA_MAGIC 0x45585401
|
|
||||||
#define SVE_MAGIC 0x53564501
|
|
||||||
struct _aarch64_ctx {
|
|
||||||
unsigned int magic;
|
|
||||||
unsigned int size;
|
|
||||||
};
|
|
||||||
struct fpsimd_context {
|
|
||||||
struct _aarch64_ctx head;
|
|
||||||
unsigned int fpsr;
|
|
||||||
unsigned int fpcr;
|
|
||||||
long double vregs[32];
|
|
||||||
};
|
|
||||||
struct esr_context {
|
|
||||||
struct _aarch64_ctx head;
|
|
||||||
unsigned long esr;
|
|
||||||
};
|
|
||||||
struct extra_context {
|
|
||||||
struct _aarch64_ctx head;
|
|
||||||
unsigned long datap;
|
|
||||||
unsigned int size;
|
|
||||||
unsigned int __reserved[3];
|
|
||||||
};
|
|
||||||
struct sve_context {
|
|
||||||
struct _aarch64_ctx head;
|
|
||||||
unsigned short vl;
|
|
||||||
unsigned short __reserved[3];
|
|
||||||
};
|
|
||||||
#define SVE_VQ_BYTES 16
|
|
||||||
#define SVE_VQ_MIN 1
|
|
||||||
#define SVE_VQ_MAX 512
|
|
||||||
#define SVE_VL_MIN (SVE_VQ_MIN * SVE_VQ_BYTES)
|
|
||||||
#define SVE_VL_MAX (SVE_VQ_MAX * SVE_VQ_BYTES)
|
|
||||||
#define SVE_NUM_ZREGS 32
|
|
||||||
#define SVE_NUM_PREGS 16
|
|
||||||
#define sve_vl_valid(vl) \
|
|
||||||
((vl) % SVE_VQ_BYTES == 0 && (vl) >= SVE_VL_MIN && (vl) <= SVE_VL_MAX)
|
|
||||||
#define sve_vq_from_vl(vl) ((vl) / SVE_VQ_BYTES)
|
|
||||||
#define sve_vl_from_vq(vq) ((vq) * SVE_VQ_BYTES)
|
|
||||||
#define SVE_SIG_ZREG_SIZE(vq) ((unsigned)(vq) * SVE_VQ_BYTES)
|
|
||||||
#define SVE_SIG_PREG_SIZE(vq) ((unsigned)(vq) * (SVE_VQ_BYTES / 8))
|
|
||||||
#define SVE_SIG_FFR_SIZE(vq) SVE_SIG_PREG_SIZE(vq)
|
|
||||||
#define SVE_SIG_REGS_OFFSET \
|
|
||||||
((sizeof(struct sve_context) + (SVE_VQ_BYTES - 1)) \
|
|
||||||
/ SVE_VQ_BYTES * SVE_VQ_BYTES)
|
|
||||||
#define SVE_SIG_ZREGS_OFFSET SVE_SIG_REGS_OFFSET
|
|
||||||
#define SVE_SIG_ZREG_OFFSET(vq, n) \
|
|
||||||
(SVE_SIG_ZREGS_OFFSET + SVE_SIG_ZREG_SIZE(vq) * (n))
|
|
||||||
#define SVE_SIG_ZREGS_SIZE(vq) \
|
|
||||||
(SVE_SIG_ZREG_OFFSET(vq, SVE_NUM_ZREGS) - SVE_SIG_ZREGS_OFFSET)
|
|
||||||
#define SVE_SIG_PREGS_OFFSET(vq) \
|
|
||||||
(SVE_SIG_ZREGS_OFFSET + SVE_SIG_ZREGS_SIZE(vq))
|
|
||||||
#define SVE_SIG_PREG_OFFSET(vq, n) \
|
|
||||||
(SVE_SIG_PREGS_OFFSET(vq) + SVE_SIG_PREG_SIZE(vq) * (n))
|
|
||||||
#define SVE_SIG_PREGS_SIZE(vq) \
|
|
||||||
(SVE_SIG_PREG_OFFSET(vq, SVE_NUM_PREGS) - SVE_SIG_PREGS_OFFSET(vq))
|
|
||||||
#define SVE_SIG_FFR_OFFSET(vq) \
|
|
||||||
(SVE_SIG_PREGS_OFFSET(vq) + SVE_SIG_PREGS_SIZE(vq))
|
|
||||||
#define SVE_SIG_REGS_SIZE(vq) \
|
|
||||||
(SVE_SIG_FFR_OFFSET(vq) + SVE_SIG_FFR_SIZE(vq) - SVE_SIG_REGS_OFFSET)
|
|
||||||
#define SVE_SIG_CONTEXT_SIZE(vq) (SVE_SIG_REGS_OFFSET + SVE_SIG_REGS_SIZE(vq))
|
|
||||||
#else
|
|
||||||
typedef struct {
|
|
||||||
long double __regs[18+256];
|
|
||||||
} mcontext_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct sigaltstack {
|
|
||||||
void *ss_sp;
|
|
||||||
int ss_flags;
|
|
||||||
size_t ss_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct __ucontext {
|
|
||||||
unsigned long uc_flags;
|
|
||||||
struct __ucontext *uc_link;
|
|
||||||
stack_t uc_stack;
|
|
||||||
sigset_t uc_sigmask;
|
|
||||||
mcontext_t uc_mcontext;
|
|
||||||
} ucontext_t;
|
|
||||||
|
|
||||||
#define SA_NOCLDSTOP 1
|
|
||||||
#define SA_NOCLDWAIT 2
|
|
||||||
#define SA_SIGINFO 4
|
|
||||||
#define SA_ONSTACK 0x08000000
|
|
||||||
#define SA_RESTART 0x10000000
|
|
||||||
#define SA_NODEFER 0x40000000
|
|
||||||
#define SA_RESETHAND 0x80000000
|
|
||||||
#define SA_RESTORER 0x04000000
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SIGHUP 1
|
|
||||||
#define SIGINT 2
|
|
||||||
#define SIGQUIT 3
|
|
||||||
#define SIGILL 4
|
|
||||||
#define SIGTRAP 5
|
|
||||||
#define SIGABRT 6
|
|
||||||
#define SIGIOT SIGABRT
|
|
||||||
#define SIGBUS 7
|
|
||||||
#define SIGFPE 8
|
|
||||||
#define SIGKILL 9
|
|
||||||
#define SIGUSR1 10
|
|
||||||
#define SIGSEGV 11
|
|
||||||
#define SIGUSR2 12
|
|
||||||
#define SIGPIPE 13
|
|
||||||
#define SIGALRM 14
|
|
||||||
#define SIGTERM 15
|
|
||||||
#define SIGSTKFLT 16
|
|
||||||
#define SIGCHLD 17
|
|
||||||
#define SIGCONT 18
|
|
||||||
#define SIGSTOP 19
|
|
||||||
#define SIGTSTP 20
|
|
||||||
#define SIGTTIN 21
|
|
||||||
#define SIGTTOU 22
|
|
||||||
#define SIGURG 23
|
|
||||||
#define SIGXCPU 24
|
|
||||||
#define SIGXFSZ 25
|
|
||||||
#define SIGVTALRM 26
|
|
||||||
#define SIGPROF 27
|
|
||||||
#define SIGWINCH 28
|
|
||||||
#define SIGIO 29
|
|
||||||
#define SIGPOLL 29
|
|
||||||
#define SIGPWR 30
|
|
||||||
#define SIGSYS 31
|
|
||||||
#define SIGUNUSED SIGSYS
|
|
||||||
|
|
||||||
#define _NSIG 65
|
|
|
@ -1,18 +0,0 @@
|
||||||
struct stat {
|
|
||||||
dev_t st_dev;
|
|
||||||
ino_t st_ino;
|
|
||||||
mode_t st_mode;
|
|
||||||
nlink_t st_nlink;
|
|
||||||
uid_t st_uid;
|
|
||||||
gid_t st_gid;
|
|
||||||
dev_t st_rdev;
|
|
||||||
unsigned long __pad;
|
|
||||||
off_t st_size;
|
|
||||||
blksize_t st_blksize;
|
|
||||||
int __pad2;
|
|
||||||
blkcnt_t st_blocks;
|
|
||||||
struct timespec st_atim;
|
|
||||||
struct timespec st_mtim;
|
|
||||||
struct timespec st_ctim;
|
|
||||||
unsigned __unused[2];
|
|
||||||
};
|
|
|
@ -1,20 +0,0 @@
|
||||||
typedef int32_t int_fast16_t;
|
|
||||||
typedef int32_t int_fast32_t;
|
|
||||||
typedef uint32_t uint_fast16_t;
|
|
||||||
typedef uint32_t uint_fast32_t;
|
|
||||||
|
|
||||||
#define INT_FAST16_MIN INT32_MIN
|
|
||||||
#define INT_FAST32_MIN INT32_MIN
|
|
||||||
|
|
||||||
#define INT_FAST16_MAX INT32_MAX
|
|
||||||
#define INT_FAST32_MAX INT32_MAX
|
|
||||||
|
|
||||||
#define UINT_FAST16_MAX UINT32_MAX
|
|
||||||
#define UINT_FAST32_MAX UINT32_MAX
|
|
||||||
|
|
||||||
#define INTPTR_MIN INT64_MIN
|
|
||||||
#define INTPTR_MAX INT64_MAX
|
|
||||||
#define UINTPTR_MAX UINT64_MAX
|
|
||||||
#define PTRDIFF_MIN INT64_MIN
|
|
||||||
#define PTRDIFF_MAX INT64_MAX
|
|
||||||
#define SIZE_MAX UINT64_MAX
|
|
|
@ -1,16 +0,0 @@
|
||||||
struct user_regs_struct {
|
|
||||||
unsigned long long regs[31];
|
|
||||||
unsigned long long sp;
|
|
||||||
unsigned long long pc;
|
|
||||||
unsigned long long pstate;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct user_fpsimd_struct {
|
|
||||||
long double vregs[32];
|
|
||||||
unsigned int fpsr;
|
|
||||||
unsigned int fpcr;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ELF_NREG 34
|
|
||||||
typedef unsigned long elf_greg_t, elf_gregset_t[ELF_NREG];
|
|
||||||
typedef struct user_fpsimd_struct elf_fpregset_t;
|
|
|
@ -1,15 +0,0 @@
|
||||||
__asm__(
|
|
||||||
".text \n"
|
|
||||||
".global " START "\n"
|
|
||||||
".type " START ",%function\n"
|
|
||||||
START ":\n"
|
|
||||||
" mov x29, #0\n"
|
|
||||||
" mov x30, #0\n"
|
|
||||||
" mov x0, sp\n"
|
|
||||||
".weak _DYNAMIC\n"
|
|
||||||
".hidden _DYNAMIC\n"
|
|
||||||
" adrp x1, _DYNAMIC\n"
|
|
||||||
" add x1, x1, #:lo12:_DYNAMIC\n"
|
|
||||||
" and sp, x0, #-16\n"
|
|
||||||
" b " START "_c\n"
|
|
||||||
);
|
|
|
@ -1,25 +0,0 @@
|
||||||
#define fp_barrierf fp_barrierf
|
|
||||||
static inline float fp_barrierf(float x)
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("" : "+w"(x));
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define fp_barrier fp_barrier
|
|
||||||
static inline double fp_barrier(double x)
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("" : "+w"(x));
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define fp_force_evalf fp_force_evalf
|
|
||||||
static inline void fp_force_evalf(float x)
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("" : "+w"(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define fp_force_eval fp_force_eval
|
|
||||||
static inline void fp_force_eval(double x)
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("" : "+w"(x));
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
struct kstat {
|
|
||||||
dev_t st_dev;
|
|
||||||
ino_t st_ino;
|
|
||||||
mode_t st_mode;
|
|
||||||
nlink_t st_nlink;
|
|
||||||
uid_t st_uid;
|
|
||||||
gid_t st_gid;
|
|
||||||
dev_t st_rdev;
|
|
||||||
unsigned long __pad;
|
|
||||||
off_t st_size;
|
|
||||||
blksize_t st_blksize;
|
|
||||||
int __pad2;
|
|
||||||
blkcnt_t st_blocks;
|
|
||||||
long st_atime_sec;
|
|
||||||
long st_atime_nsec;
|
|
||||||
long st_mtime_sec;
|
|
||||||
long st_mtime_nsec;
|
|
||||||
long st_ctime_sec;
|
|
||||||
long st_ctime_nsec;
|
|
||||||
unsigned __unused[2];
|
|
||||||
};
|
|
|
@ -1,12 +0,0 @@
|
||||||
static inline struct pthread *__pthread_self()
|
|
||||||
{
|
|
||||||
char *self;
|
|
||||||
__asm__ ("mrs %0,tpidr_el0" : "=r"(self));
|
|
||||||
return (void*)(self - sizeof(struct pthread));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TLS_ABOVE_TP
|
|
||||||
#define GAP_ABOVE_TP 16
|
|
||||||
#define TP_ADJ(p) ((char *)(p) + sizeof(struct pthread))
|
|
||||||
|
|
||||||
#define MC_PC pc
|
|
|
@ -1,24 +0,0 @@
|
||||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
||||||
#define ENDIAN_SUFFIX "_be"
|
|
||||||
#else
|
|
||||||
#define ENDIAN_SUFFIX ""
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LDSO_ARCH "aarch64" ENDIAN_SUFFIX
|
|
||||||
|
|
||||||
#define NO_LEGACY_INITFINI
|
|
||||||
|
|
||||||
#define TPOFF_K 0
|
|
||||||
|
|
||||||
#define REL_SYMBOLIC R_AARCH64_ABS64
|
|
||||||
#define REL_GOT R_AARCH64_GLOB_DAT
|
|
||||||
#define REL_PLT R_AARCH64_JUMP_SLOT
|
|
||||||
#define REL_RELATIVE R_AARCH64_RELATIVE
|
|
||||||
#define REL_COPY R_AARCH64_COPY
|
|
||||||
#define REL_DTPMOD R_AARCH64_TLS_DTPMOD64
|
|
||||||
#define REL_DTPOFF R_AARCH64_TLS_DTPREL64
|
|
||||||
#define REL_TPOFF R_AARCH64_TLS_TPREL64
|
|
||||||
#define REL_TLSDESC R_AARCH64_TLSDESC
|
|
||||||
|
|
||||||
#define CRTJMP(pc,sp) __asm__ __volatile__( \
|
|
||||||
"mov sp,%1 ; br %0" : : "r"(pc), "r"(sp) : "memory" )
|
|
|
@ -1,78 +0,0 @@
|
||||||
#define __SYSCALL_LL_E(x) (x)
|
|
||||||
#define __SYSCALL_LL_O(x) (x)
|
|
||||||
|
|
||||||
#define __asm_syscall(...) do { \
|
|
||||||
__asm__ __volatile__ ( "svc 0" \
|
|
||||||
: "=r"(x0) : __VA_ARGS__ : "memory", "cc"); \
|
|
||||||
return x0; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
inline long __syscall0(long n)
|
|
||||||
{
|
|
||||||
register long x8 __asm__("x8") = n;
|
|
||||||
register long x0 __asm__("x0");
|
|
||||||
__asm_syscall("r"(x8));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall1(long n, long a)
|
|
||||||
{
|
|
||||||
register long x8 __asm__("x8") = n;
|
|
||||||
register long x0 __asm__("x0") = a;
|
|
||||||
__asm_syscall("r"(x8), "0"(x0));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall2(long n, long a, long b)
|
|
||||||
{
|
|
||||||
register long x8 __asm__("x8") = n;
|
|
||||||
register long x0 __asm__("x0") = a;
|
|
||||||
register long x1 __asm__("x1") = b;
|
|
||||||
__asm_syscall("r"(x8), "0"(x0), "r"(x1));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall3(long n, long a, long b, long c)
|
|
||||||
{
|
|
||||||
register long x8 __asm__("x8") = n;
|
|
||||||
register long x0 __asm__("x0") = a;
|
|
||||||
register long x1 __asm__("x1") = b;
|
|
||||||
register long x2 __asm__("x2") = c;
|
|
||||||
__asm_syscall("r"(x8), "0"(x0), "r"(x1), "r"(x2));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall4(long n, long a, long b, long c, long d)
|
|
||||||
{
|
|
||||||
register long x8 __asm__("x8") = n;
|
|
||||||
register long x0 __asm__("x0") = a;
|
|
||||||
register long x1 __asm__("x1") = b;
|
|
||||||
register long x2 __asm__("x2") = c;
|
|
||||||
register long x3 __asm__("x3") = d;
|
|
||||||
__asm_syscall("r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall5(long n, long a, long b, long c, long d, long e)
|
|
||||||
{
|
|
||||||
register long x8 __asm__("x8") = n;
|
|
||||||
register long x0 __asm__("x0") = a;
|
|
||||||
register long x1 __asm__("x1") = b;
|
|
||||||
register long x2 __asm__("x2") = c;
|
|
||||||
register long x3 __asm__("x3") = d;
|
|
||||||
register long x4 __asm__("x4") = e;
|
|
||||||
__asm_syscall("r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall6(long n, long a, long b, long c, long d, long e, long f)
|
|
||||||
{
|
|
||||||
register long x8 __asm__("x8") = n;
|
|
||||||
register long x0 __asm__("x0") = a;
|
|
||||||
register long x1 __asm__("x1") = b;
|
|
||||||
register long x2 __asm__("x2") = c;
|
|
||||||
register long x3 __asm__("x3") = d;
|
|
||||||
register long x4 __asm__("x4") = e;
|
|
||||||
register long x5 __asm__("x5") = f;
|
|
||||||
__asm_syscall("r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define VDSO_USEFUL
|
|
||||||
#define VDSO_CGT_SYM "__kernel_clock_gettime"
|
|
||||||
#define VDSO_CGT_VER "LINUX_2.6.39"
|
|
||||||
|
|
||||||
#define IPC_64 0
|
|
|
@ -1,109 +0,0 @@
|
||||||
#include "libc.h"
|
|
||||||
|
|
||||||
#if __ARM_ARCH_4__ || __ARM_ARCH_4T__ || __ARM_ARCH == 4
|
|
||||||
#define BLX "mov lr,pc\n\tbx"
|
|
||||||
#else
|
|
||||||
#define BLX "blx"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern hidden uintptr_t __a_cas_ptr, __a_barrier_ptr;
|
|
||||||
|
|
||||||
#if ((__ARM_ARCH_6__ || __ARM_ARCH_6K__ || __ARM_ARCH_6KZ__ || __ARM_ARCH_6ZK__) && !__thumb__) \
|
|
||||||
|| __ARM_ARCH_6T2__ || __ARM_ARCH_7A__ || __ARM_ARCH_7R__ || __ARM_ARCH >= 7
|
|
||||||
|
|
||||||
#define a_ll a_ll
|
|
||||||
static inline int a_ll(volatile int *p)
|
|
||||||
{
|
|
||||||
int v;
|
|
||||||
__asm__ __volatile__ ("ldrex %0, %1" : "=r"(v) : "Q"(*p));
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define a_sc a_sc
|
|
||||||
static inline int a_sc(volatile int *p, int v)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
__asm__ __volatile__ ("strex %0,%2,%1" : "=&r"(r), "=Q"(*p) : "r"(v) : "memory");
|
|
||||||
return !r;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if __ARM_ARCH_7A__ || __ARM_ARCH_7R__ || __ARM_ARCH >= 7
|
|
||||||
|
|
||||||
#define a_barrier a_barrier
|
|
||||||
static inline void a_barrier()
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("dmb ish" : : : "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define a_pre_llsc a_barrier
|
|
||||||
#define a_post_llsc a_barrier
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define a_cas a_cas
|
|
||||||
static inline int a_cas(volatile int *p, int t, int s)
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("TODO");
|
|
||||||
//TODO for (;;) {
|
|
||||||
//TODO register int r0 __asm__("r0") = t;
|
|
||||||
//TODO register int r1 __asm__("r1") = s;
|
|
||||||
//TODO register volatile int *r2 __asm__("r2") = p;
|
|
||||||
//TODO register uintptr_t r3 __asm__("r3") = __a_cas_ptr;
|
|
||||||
//TODO int old;
|
|
||||||
//TODO __asm__ __volatile__ (
|
|
||||||
//TODO BLX " r3"
|
|
||||||
//TODO : "+r"(r0), "+r"(r3) : "r"(r1), "r"(r2)
|
|
||||||
//TODO : "memory", "lr", "ip", "cc" );
|
|
||||||
//TODO if (!r0) return t;
|
|
||||||
//TODO if ((old=*p)!=t) return old;
|
|
||||||
//TODO }
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef a_barrier
|
|
||||||
#define a_barrier a_barrier
|
|
||||||
static inline void a_barrier()
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("TODO");
|
|
||||||
//TODO register uintptr_t ip __asm__("ip") = __a_barrier_ptr;
|
|
||||||
//TODO __asm__ __volatile__( BLX " ip" : "+r"(ip) : : "memory", "cc", "lr" );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define a_crash a_crash
|
|
||||||
static inline void a_crash()
|
|
||||||
{
|
|
||||||
__asm__ __volatile__(
|
|
||||||
#ifndef __thumb__
|
|
||||||
".word 0xe7f000f0"
|
|
||||||
#else
|
|
||||||
".short 0xdeff"
|
|
||||||
#endif
|
|
||||||
: : : "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
#if __ARM_ARCH >= 5 && (!__thumb__ || __thumb2__)
|
|
||||||
|
|
||||||
#define a_clz_32 a_clz_32
|
|
||||||
static inline int a_clz_32(uint32_t x)
|
|
||||||
{
|
|
||||||
__asm__ ("clz %0, %1" : "=r"(x) : "r"(x));
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if __ARM_ARCH_6T2__ || __ARM_ARCH_7A__ || __ARM_ARCH_7R__ || __ARM_ARCH >= 7
|
|
||||||
|
|
||||||
#define a_ctz_32 a_ctz_32
|
|
||||||
static inline int a_ctz_32(uint32_t x)
|
|
||||||
{
|
|
||||||
uint32_t xr;
|
|
||||||
__asm__ ("rbit %0, %1" : "=r"(xr) : "r"(x));
|
|
||||||
return a_clz_32(xr);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,40 +0,0 @@
|
||||||
#define O_CREAT 0100
|
|
||||||
#define O_EXCL 0200
|
|
||||||
#define O_NOCTTY 0400
|
|
||||||
#define O_TRUNC 01000
|
|
||||||
#define O_APPEND 02000
|
|
||||||
#define O_NONBLOCK 04000
|
|
||||||
#define O_DSYNC 010000
|
|
||||||
#define O_SYNC 04010000
|
|
||||||
#define O_RSYNC 04010000
|
|
||||||
#define O_DIRECTORY 040000
|
|
||||||
#define O_NOFOLLOW 0100000
|
|
||||||
#define O_CLOEXEC 02000000
|
|
||||||
|
|
||||||
#define O_ASYNC 020000
|
|
||||||
#define O_DIRECT 0200000
|
|
||||||
#define O_LARGEFILE 0400000
|
|
||||||
#define O_NOATIME 01000000
|
|
||||||
#define O_PATH 010000000
|
|
||||||
#define O_TMPFILE 020040000
|
|
||||||
#define O_NDELAY O_NONBLOCK
|
|
||||||
|
|
||||||
#define F_DUPFD 0
|
|
||||||
#define F_GETFD 1
|
|
||||||
#define F_SETFD 2
|
|
||||||
#define F_GETFL 3
|
|
||||||
#define F_SETFL 4
|
|
||||||
|
|
||||||
#define F_SETOWN 8
|
|
||||||
#define F_GETOWN 9
|
|
||||||
#define F_SETSIG 10
|
|
||||||
#define F_GETSIG 11
|
|
||||||
|
|
||||||
#define F_GETLK 12
|
|
||||||
#define F_SETLK 13
|
|
||||||
#define F_SETLKW 14
|
|
||||||
|
|
||||||
#define F_SETOWN_EX 15
|
|
||||||
#define F_GETOWN_EX 16
|
|
||||||
|
|
||||||
#define F_GETOWNER_UIDS 17
|
|
|
@ -1,23 +0,0 @@
|
||||||
#ifndef __ARM_PCS_VFP
|
|
||||||
#define FE_ALL_EXCEPT 0
|
|
||||||
#define FE_TONEAREST 0
|
|
||||||
#else
|
|
||||||
#define FE_INVALID 1
|
|
||||||
#define FE_DIVBYZERO 2
|
|
||||||
#define FE_OVERFLOW 4
|
|
||||||
#define FE_UNDERFLOW 8
|
|
||||||
#define FE_INEXACT 16
|
|
||||||
#define FE_ALL_EXCEPT 31
|
|
||||||
#define FE_TONEAREST 0
|
|
||||||
#define FE_DOWNWARD 0x800000
|
|
||||||
#define FE_UPWARD 0x400000
|
|
||||||
#define FE_TOWARDZERO 0xc00000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef unsigned long fexcept_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned long __cw;
|
|
||||||
} fenv_t;
|
|
||||||
|
|
||||||
#define FE_DFL_ENV ((const fenv_t *) -1)
|
|
|
@ -1,16 +0,0 @@
|
||||||
#define FLT_EVAL_METHOD 0
|
|
||||||
|
|
||||||
#define LDBL_TRUE_MIN 4.94065645841246544177e-324L
|
|
||||||
#define LDBL_MIN 2.22507385850720138309e-308L
|
|
||||||
#define LDBL_MAX 1.79769313486231570815e+308L
|
|
||||||
#define LDBL_EPSILON 2.22044604925031308085e-16L
|
|
||||||
|
|
||||||
#define LDBL_MANT_DIG 53
|
|
||||||
#define LDBL_MIN_EXP (-1021)
|
|
||||||
#define LDBL_MAX_EXP 1024
|
|
||||||
|
|
||||||
#define LDBL_DIG 15
|
|
||||||
#define LDBL_MIN_10_EXP (-307)
|
|
||||||
#define LDBL_MAX_10_EXP 308
|
|
||||||
|
|
||||||
#define DECIMAL_DIG 17
|
|
|
@ -1,53 +0,0 @@
|
||||||
#define HWCAP_SWP (1 << 0)
|
|
||||||
#define HWCAP_HALF (1 << 1)
|
|
||||||
#define HWCAP_THUMB (1 << 2)
|
|
||||||
#define HWCAP_26BIT (1 << 3)
|
|
||||||
#define HWCAP_FAST_MULT (1 << 4)
|
|
||||||
#define HWCAP_FPA (1 << 5)
|
|
||||||
#define HWCAP_VFP (1 << 6)
|
|
||||||
#define HWCAP_EDSP (1 << 7)
|
|
||||||
#define HWCAP_JAVA (1 << 8)
|
|
||||||
#define HWCAP_IWMMXT (1 << 9)
|
|
||||||
#define HWCAP_CRUNCH (1 << 10)
|
|
||||||
#define HWCAP_THUMBEE (1 << 11)
|
|
||||||
#define HWCAP_NEON (1 << 12)
|
|
||||||
#define HWCAP_VFPv3 (1 << 13)
|
|
||||||
#define HWCAP_VFPv3D16 (1 << 14)
|
|
||||||
#define HWCAP_TLS (1 << 15)
|
|
||||||
#define HWCAP_VFPv4 (1 << 16)
|
|
||||||
#define HWCAP_IDIVA (1 << 17)
|
|
||||||
#define HWCAP_IDIVT (1 << 18)
|
|
||||||
#define HWCAP_VFPD32 (1 << 19)
|
|
||||||
#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
|
|
||||||
#define HWCAP_LPAE (1 << 20)
|
|
||||||
#define HWCAP_EVTSTRM (1 << 21)
|
|
||||||
|
|
||||||
#define HWCAP2_AES (1 << 0)
|
|
||||||
#define HWCAP2_PMULL (1 << 1)
|
|
||||||
#define HWCAP2_SHA1 (1 << 2)
|
|
||||||
#define HWCAP2_SHA2 (1 << 3)
|
|
||||||
#define HWCAP2_CRC32 (1 << 4)
|
|
||||||
|
|
||||||
#define HWCAP_ARM_SWP (1 << 0)
|
|
||||||
#define HWCAP_ARM_HALF (1 << 1)
|
|
||||||
#define HWCAP_ARM_THUMB (1 << 2)
|
|
||||||
#define HWCAP_ARM_26BIT (1 << 3)
|
|
||||||
#define HWCAP_ARM_FAST_MULT (1 << 4)
|
|
||||||
#define HWCAP_ARM_FPA (1 << 5)
|
|
||||||
#define HWCAP_ARM_VFP (1 << 6)
|
|
||||||
#define HWCAP_ARM_EDSP (1 << 7)
|
|
||||||
#define HWCAP_ARM_JAVA (1 << 8)
|
|
||||||
#define HWCAP_ARM_IWMMXT (1 << 9)
|
|
||||||
#define HWCAP_ARM_CRUNCH (1 << 10)
|
|
||||||
#define HWCAP_ARM_THUMBEE (1 << 11)
|
|
||||||
#define HWCAP_ARM_NEON (1 << 12)
|
|
||||||
#define HWCAP_ARM_VFPv3 (1 << 13)
|
|
||||||
#define HWCAP_ARM_VFPv3D16 (1 << 14)
|
|
||||||
#define HWCAP_ARM_TLS (1 << 15)
|
|
||||||
#define HWCAP_ARM_VFPv4 (1 << 16)
|
|
||||||
#define HWCAP_ARM_IDIVA (1 << 17)
|
|
||||||
#define HWCAP_ARM_IDIVT (1 << 18)
|
|
||||||
#define HWCAP_ARM_VFPD32 (1 << 19)
|
|
||||||
#define HWCAP_ARM_IDIV (HWCAP_ARM_IDIVA | HWCAP_ARM_IDIVT)
|
|
||||||
#define HWCAP_ARM_LPAE (1 << 20)
|
|
||||||
#define HWCAP_ARM_EVTSTRM (1 << 21)
|
|
|
@ -1,2 +0,0 @@
|
||||||
#undef FIOQSIZE
|
|
||||||
#define FIOQSIZE 0x545e
|
|
|
@ -1 +0,0 @@
|
||||||
#define IPC_STAT 0x102
|
|
|
@ -1,18 +0,0 @@
|
||||||
struct msqid_ds {
|
|
||||||
struct ipc_perm msg_perm;
|
|
||||||
unsigned long __msg_stime_lo;
|
|
||||||
unsigned long __msg_stime_hi;
|
|
||||||
unsigned long __msg_rtime_lo;
|
|
||||||
unsigned long __msg_rtime_hi;
|
|
||||||
unsigned long __msg_ctime_lo;
|
|
||||||
unsigned long __msg_ctime_hi;
|
|
||||||
unsigned long msg_cbytes;
|
|
||||||
msgqnum_t msg_qnum;
|
|
||||||
msglen_t msg_qbytes;
|
|
||||||
pid_t msg_lspid;
|
|
||||||
pid_t msg_lrpid;
|
|
||||||
unsigned long __unused[2];
|
|
||||||
time_t msg_stime;
|
|
||||||
time_t msg_rtime;
|
|
||||||
time_t msg_ctime;
|
|
||||||
};
|
|
|
@ -1,2 +0,0 @@
|
||||||
#define _POSIX_V6_ILP32_OFFBIG 1
|
|
||||||
#define _POSIX_V7_ILP32_OFFBIG 1
|
|
|
@ -1,25 +0,0 @@
|
||||||
#define PTRACE_GETWMMXREGS 18
|
|
||||||
#define PTRACE_SETWMMXREGS 19
|
|
||||||
#define PTRACE_GET_THREAD_AREA 22
|
|
||||||
#define PTRACE_SET_SYSCALL 23
|
|
||||||
#define PTRACE_GETCRUNCHREGS 25
|
|
||||||
#define PTRACE_SETCRUNCHREGS 26
|
|
||||||
#define PTRACE_GETVFPREGS 27
|
|
||||||
#define PTRACE_SETVFPREGS 28
|
|
||||||
#define PTRACE_GETHBPREGS 29
|
|
||||||
#define PTRACE_SETHBPREGS 30
|
|
||||||
#define PTRACE_GETFDPIC 31
|
|
||||||
#define PTRACE_GETFDPIC_EXEC 0
|
|
||||||
#define PTRACE_GETFDPIC_INTERP 1
|
|
||||||
|
|
||||||
#define PT_GETWMMXREGS PTRACE_GETWMMXREGS
|
|
||||||
#define PT_SETWMMXREGS PTRACE_SETWMMXREGS
|
|
||||||
#define PT_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
|
|
||||||
#define PT_SET_SYSCALL PTRACE_SET_SYSCALL
|
|
||||||
#define PT_GETCRUNCHREGS PTRACE_GETCRUNCHREGS
|
|
||||||
#define PT_SETCRUNCHREGS PTRACE_SETCRUNCHREGS
|
|
||||||
#define PT_GETVFPREGS PTRACE_GETVFPREGS
|
|
||||||
#define PT_SETVFPREGS PTRACE_SETVFPREGS
|
|
||||||
#define PT_GETHBPREGS PTRACE_GETHBPREGS
|
|
||||||
#define PT_SETHBPREGS PTRACE_SETHBPREGS
|
|
||||||
#define PT_GETFDPIC PTRACE_GETFDPIC
|
|
|
@ -1,3 +0,0 @@
|
||||||
#undef __WORDSIZE
|
|
||||||
#define __WORDSIZE 32
|
|
||||||
/* FIXME */
|
|
|
@ -1,18 +0,0 @@
|
||||||
struct semid_ds {
|
|
||||||
struct ipc_perm sem_perm;
|
|
||||||
unsigned long __sem_otime_lo;
|
|
||||||
unsigned long __sem_otime_hi;
|
|
||||||
unsigned long __sem_ctime_lo;
|
|
||||||
unsigned long __sem_ctime_hi;
|
|
||||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
||||||
unsigned short sem_nsems;
|
|
||||||
char __sem_nsems_pad[sizeof(long)-sizeof(short)];
|
|
||||||
#else
|
|
||||||
char __sem_nsems_pad[sizeof(long)-sizeof(short)];
|
|
||||||
unsigned short sem_nsems;
|
|
||||||
#endif
|
|
||||||
long __unused3;
|
|
||||||
long __unused4;
|
|
||||||
time_t sem_otime;
|
|
||||||
time_t sem_ctime;
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
typedef unsigned long long __jmp_buf[32];
|
|
|
@ -1,31 +0,0 @@
|
||||||
#define SHMLBA 4096
|
|
||||||
|
|
||||||
struct shmid_ds {
|
|
||||||
struct ipc_perm shm_perm;
|
|
||||||
size_t shm_segsz;
|
|
||||||
unsigned long __shm_atime_lo;
|
|
||||||
unsigned long __shm_atime_hi;
|
|
||||||
unsigned long __shm_dtime_lo;
|
|
||||||
unsigned long __shm_dtime_hi;
|
|
||||||
unsigned long __shm_ctime_lo;
|
|
||||||
unsigned long __shm_ctime_hi;
|
|
||||||
pid_t shm_cpid;
|
|
||||||
pid_t shm_lpid;
|
|
||||||
unsigned long shm_nattch;
|
|
||||||
unsigned long __pad1;
|
|
||||||
unsigned long __pad2;
|
|
||||||
unsigned long __pad3;
|
|
||||||
time_t shm_atime;
|
|
||||||
time_t shm_dtime;
|
|
||||||
time_t shm_ctime;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct shminfo {
|
|
||||||
unsigned long shmmax, shmmin, shmmni, shmseg, shmall, __unused[4];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct shm_info {
|
|
||||||
int __used_ids;
|
|
||||||
unsigned long shm_tot, shm_rss, shm_swp;
|
|
||||||
unsigned long __swap_attempts, __swap_successes;
|
|
||||||
};
|
|
|
@ -1,86 +0,0 @@
|
||||||
#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
|
|
||||||
|| defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
|
||||||
|
|
||||||
#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
|
||||||
#define MINSIGSTKSZ 2048
|
|
||||||
#define SIGSTKSZ 8192
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
|
||||||
typedef int greg_t, gregset_t[18];
|
|
||||||
typedef struct sigcontext {
|
|
||||||
unsigned long trap_no, error_code, oldmask;
|
|
||||||
unsigned long arm_r0, arm_r1, arm_r2, arm_r3;
|
|
||||||
unsigned long arm_r4, arm_r5, arm_r6, arm_r7;
|
|
||||||
unsigned long arm_r8, arm_r9, arm_r10, arm_fp;
|
|
||||||
unsigned long arm_ip, arm_sp, arm_lr, arm_pc;
|
|
||||||
unsigned long arm_cpsr, fault_address;
|
|
||||||
} mcontext_t;
|
|
||||||
#else
|
|
||||||
typedef struct {
|
|
||||||
unsigned long __regs[21];
|
|
||||||
} mcontext_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct sigaltstack {
|
|
||||||
void *ss_sp;
|
|
||||||
int ss_flags;
|
|
||||||
size_t ss_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct __ucontext {
|
|
||||||
unsigned long uc_flags;
|
|
||||||
struct __ucontext *uc_link;
|
|
||||||
stack_t uc_stack;
|
|
||||||
mcontext_t uc_mcontext;
|
|
||||||
sigset_t uc_sigmask;
|
|
||||||
unsigned long long uc_regspace[64];
|
|
||||||
} ucontext_t;
|
|
||||||
|
|
||||||
#define SA_NOCLDSTOP 1
|
|
||||||
#define SA_NOCLDWAIT 2
|
|
||||||
#define SA_SIGINFO 4
|
|
||||||
#define SA_ONSTACK 0x08000000
|
|
||||||
#define SA_RESTART 0x10000000
|
|
||||||
#define SA_NODEFER 0x40000000
|
|
||||||
#define SA_RESETHAND 0x80000000
|
|
||||||
#define SA_RESTORER 0x04000000
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SIGHUP 1
|
|
||||||
#define SIGINT 2
|
|
||||||
#define SIGQUIT 3
|
|
||||||
#define SIGILL 4
|
|
||||||
#define SIGTRAP 5
|
|
||||||
#define SIGABRT 6
|
|
||||||
#define SIGIOT SIGABRT
|
|
||||||
#define SIGBUS 7
|
|
||||||
#define SIGFPE 8
|
|
||||||
#define SIGKILL 9
|
|
||||||
#define SIGUSR1 10
|
|
||||||
#define SIGSEGV 11
|
|
||||||
#define SIGUSR2 12
|
|
||||||
#define SIGPIPE 13
|
|
||||||
#define SIGALRM 14
|
|
||||||
#define SIGTERM 15
|
|
||||||
#define SIGSTKFLT 16
|
|
||||||
#define SIGCHLD 17
|
|
||||||
#define SIGCONT 18
|
|
||||||
#define SIGSTOP 19
|
|
||||||
#define SIGTSTP 20
|
|
||||||
#define SIGTTIN 21
|
|
||||||
#define SIGTTOU 22
|
|
||||||
#define SIGURG 23
|
|
||||||
#define SIGXCPU 24
|
|
||||||
#define SIGXFSZ 25
|
|
||||||
#define SIGVTALRM 26
|
|
||||||
#define SIGPROF 27
|
|
||||||
#define SIGWINCH 28
|
|
||||||
#define SIGIO 29
|
|
||||||
#define SIGPOLL 29
|
|
||||||
#define SIGPWR 30
|
|
||||||
#define SIGSYS 31
|
|
||||||
#define SIGUNUSED SIGSYS
|
|
||||||
|
|
||||||
#define _NSIG 65
|
|
|
@ -1,25 +0,0 @@
|
||||||
/* copied from kernel definition, but with padding replaced
|
|
||||||
* by the corresponding correctly-sized userspace types. */
|
|
||||||
|
|
||||||
struct stat {
|
|
||||||
dev_t st_dev;
|
|
||||||
int __st_dev_padding;
|
|
||||||
long __st_ino_truncated;
|
|
||||||
mode_t st_mode;
|
|
||||||
nlink_t st_nlink;
|
|
||||||
uid_t st_uid;
|
|
||||||
gid_t st_gid;
|
|
||||||
dev_t st_rdev;
|
|
||||||
int __st_rdev_padding;
|
|
||||||
off_t st_size;
|
|
||||||
blksize_t st_blksize;
|
|
||||||
blkcnt_t st_blocks;
|
|
||||||
struct {
|
|
||||||
long tv_sec;
|
|
||||||
long tv_nsec;
|
|
||||||
} __st_atim32, __st_mtim32, __st_ctim32;
|
|
||||||
ino_t st_ino;
|
|
||||||
struct timespec st_atim;
|
|
||||||
struct timespec st_mtim;
|
|
||||||
struct timespec st_ctim;
|
|
||||||
};
|
|
|
@ -1,20 +0,0 @@
|
||||||
typedef int32_t int_fast16_t;
|
|
||||||
typedef int32_t int_fast32_t;
|
|
||||||
typedef uint32_t uint_fast16_t;
|
|
||||||
typedef uint32_t uint_fast32_t;
|
|
||||||
|
|
||||||
#define INT_FAST16_MIN INT32_MIN
|
|
||||||
#define INT_FAST32_MIN INT32_MIN
|
|
||||||
|
|
||||||
#define INT_FAST16_MAX INT32_MAX
|
|
||||||
#define INT_FAST32_MAX INT32_MAX
|
|
||||||
|
|
||||||
#define UINT_FAST16_MAX UINT32_MAX
|
|
||||||
#define UINT_FAST32_MAX UINT32_MAX
|
|
||||||
|
|
||||||
#define INTPTR_MIN INT32_MIN
|
|
||||||
#define INTPTR_MAX INT32_MAX
|
|
||||||
#define UINTPTR_MAX UINT32_MAX
|
|
||||||
#define PTRDIFF_MIN INT32_MIN
|
|
||||||
#define PTRDIFF_MAX INT32_MAX
|
|
||||||
#define SIZE_MAX UINT32_MAX
|
|
|
@ -1,36 +0,0 @@
|
||||||
typedef struct user_fpregs {
|
|
||||||
struct fp_reg {
|
|
||||||
unsigned sign1:1;
|
|
||||||
unsigned unused:15;
|
|
||||||
unsigned sign2:1;
|
|
||||||
unsigned exponent:14;
|
|
||||||
unsigned j:1;
|
|
||||||
unsigned mantissa1:31;
|
|
||||||
unsigned mantissa0:32;
|
|
||||||
} fpregs[8];
|
|
||||||
unsigned fpsr:32;
|
|
||||||
unsigned fpcr:32;
|
|
||||||
unsigned char ftype[8];
|
|
||||||
unsigned int init_flag;
|
|
||||||
} elf_fpregset_t;
|
|
||||||
|
|
||||||
struct user_regs {
|
|
||||||
unsigned long uregs[18];
|
|
||||||
};
|
|
||||||
#define ELF_NGREG 18
|
|
||||||
typedef unsigned long elf_greg_t, elf_gregset_t[ELF_NGREG];
|
|
||||||
|
|
||||||
struct user {
|
|
||||||
struct user_regs regs;
|
|
||||||
int u_fpvalid;
|
|
||||||
unsigned long u_tsize, u_dsize, u_ssize;
|
|
||||||
unsigned long start_code, start_stack;
|
|
||||||
long signal;
|
|
||||||
int reserved;
|
|
||||||
struct user_regs *u_ar0;
|
|
||||||
unsigned long magic;
|
|
||||||
char u_comm[32];
|
|
||||||
int u_debugreg[8];
|
|
||||||
struct user_fpregs u_fp;
|
|
||||||
struct user_fpregs *u_fp0;
|
|
||||||
};
|
|
|
@ -1,18 +0,0 @@
|
||||||
__asm__(
|
|
||||||
".text \n"
|
|
||||||
".global " START " \n"
|
|
||||||
".type " START ",%function \n"
|
|
||||||
START ": \n"
|
|
||||||
" mov fp, #0 \n"
|
|
||||||
" mov lr, #0 \n"
|
|
||||||
" ldr a2, 1f \n"
|
|
||||||
" add a2, pc, a2 \n"
|
|
||||||
" mov a1, sp \n"
|
|
||||||
"2: and ip, a1, #-16 \n"
|
|
||||||
" mov sp, ip \n"
|
|
||||||
" bl " START "_c \n"
|
|
||||||
".weak _DYNAMIC \n"
|
|
||||||
".hidden _DYNAMIC \n"
|
|
||||||
".align 2 \n"
|
|
||||||
"1: .word _DYNAMIC-2b \n"
|
|
||||||
);
|
|
|
@ -1,21 +0,0 @@
|
||||||
struct kstat {
|
|
||||||
dev_t st_dev;
|
|
||||||
int __st_dev_padding;
|
|
||||||
long __st_ino_truncated;
|
|
||||||
mode_t st_mode;
|
|
||||||
nlink_t st_nlink;
|
|
||||||
uid_t st_uid;
|
|
||||||
gid_t st_gid;
|
|
||||||
dev_t st_rdev;
|
|
||||||
int __st_rdev_padding;
|
|
||||||
off_t st_size;
|
|
||||||
blksize_t st_blksize;
|
|
||||||
blkcnt_t st_blocks;
|
|
||||||
long st_atime_sec;
|
|
||||||
long st_atime_nsec;
|
|
||||||
long st_mtime_sec;
|
|
||||||
long st_mtime_nsec;
|
|
||||||
long st_ctime_sec;
|
|
||||||
long st_ctime_nsec;
|
|
||||||
ino_t st_ino;
|
|
||||||
};
|
|
|
@ -1,33 +0,0 @@
|
||||||
#if ((__ARM_ARCH_6K__ || __ARM_ARCH_6KZ__ || __ARM_ARCH_6ZK__) && !__thumb__) \
|
|
||||||
|| __ARM_ARCH_7A__ || __ARM_ARCH_7R__ || __ARM_ARCH >= 7
|
|
||||||
|
|
||||||
static inline pthread_t __pthread_self()
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
__asm__ ( "mrc p15,0,%0,c13,c0,3" : "=r"(p) );
|
|
||||||
return (void *)(p-sizeof(struct pthread));
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#if __ARM_ARCH_4__ || __ARM_ARCH_4T__ || __ARM_ARCH == 4
|
|
||||||
#define BLX "mov lr,pc\n\tbx"
|
|
||||||
#else
|
|
||||||
#define BLX "blx"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline pthread_t __pthread_self()
|
|
||||||
{
|
|
||||||
extern hidden uintptr_t __a_gettp_ptr;
|
|
||||||
register uintptr_t p __asm__("r0");
|
|
||||||
__asm__ ( BLX " %1" : "=r"(p) : "r"(__a_gettp_ptr) : "cc", "lr" );
|
|
||||||
return (void *)(p-sizeof(struct pthread));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define TLS_ABOVE_TP
|
|
||||||
#define GAP_ABOVE_TP 8
|
|
||||||
#define TP_ADJ(p) ((char *)(p) + sizeof(struct pthread))
|
|
||||||
|
|
||||||
#define MC_PC arm_pc
|
|
|
@ -1,32 +0,0 @@
|
||||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
||||||
#define ENDIAN_SUFFIX "eb"
|
|
||||||
#else
|
|
||||||
#define ENDIAN_SUFFIX ""
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __ARM_PCS_VFP
|
|
||||||
#define FP_SUFFIX "hf"
|
|
||||||
#else
|
|
||||||
#define FP_SUFFIX ""
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LDSO_ARCH "arm" ENDIAN_SUFFIX FP_SUFFIX
|
|
||||||
|
|
||||||
#define NO_LEGACY_INITFINI
|
|
||||||
|
|
||||||
#define TPOFF_K 0
|
|
||||||
|
|
||||||
#define REL_SYMBOLIC R_ARM_ABS32
|
|
||||||
#define REL_GOT R_ARM_GLOB_DAT
|
|
||||||
#define REL_PLT R_ARM_JUMP_SLOT
|
|
||||||
#define REL_RELATIVE R_ARM_RELATIVE
|
|
||||||
#define REL_COPY R_ARM_COPY
|
|
||||||
#define REL_DTPMOD R_ARM_TLS_DTPMOD32
|
|
||||||
#define REL_DTPOFF R_ARM_TLS_DTPOFF32
|
|
||||||
#define REL_TPOFF R_ARM_TLS_TPOFF32
|
|
||||||
#define REL_TLSDESC R_ARM_TLS_DESC
|
|
||||||
|
|
||||||
#define TLSDESC_BACKWARDS
|
|
||||||
|
|
||||||
#define CRTJMP(pc,sp) __asm__ __volatile__( \
|
|
||||||
"mov sp,%1 ; bx %0" : : "r"(pc), "r"(sp) : "memory" )
|
|
|
@ -1,103 +0,0 @@
|
||||||
#define __SYSCALL_LL_E(x) \
|
|
||||||
((union { long long ll; long l[2]; }){ .ll = x }).l[0], \
|
|
||||||
((union { long long ll; long l[2]; }){ .ll = x }).l[1]
|
|
||||||
#define __SYSCALL_LL_O(x) 0, __SYSCALL_LL_E((x))
|
|
||||||
|
|
||||||
#ifdef __thumb__
|
|
||||||
|
|
||||||
/* Avoid use of r7 in asm constraints when producing thumb code,
|
|
||||||
* since it's reserved as frame pointer and might not be supported. */
|
|
||||||
#define __ASM____R7__
|
|
||||||
#define __asm_syscall(...) do { \
|
|
||||||
__asm__ __volatile__ ( "mov %1,r7 ; mov r7,%2 ; svc 0 ; mov r7,%1" \
|
|
||||||
: "=r"(r0), "=&r"((int){0}) : __VA_ARGS__ : "memory"); \
|
|
||||||
return r0; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define __ASM____R7__ __asm__("r7")
|
|
||||||
#define __asm_syscall(...) do { \
|
|
||||||
__asm__ __volatile__ ( "svc 0" \
|
|
||||||
: "=r"(r0) : __VA_ARGS__ : "memory"); \
|
|
||||||
return r0; \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* For thumb2, we can allow 8-bit immediate syscall numbers, saving a
|
|
||||||
* register in the above dance around r7. Does not work for thumb1 where
|
|
||||||
* only movs, not mov, supports immediates, and we can't use movs because
|
|
||||||
* it doesn't support high regs. */
|
|
||||||
#ifdef __thumb2__
|
|
||||||
#define R7_OPERAND "rI"(r7)
|
|
||||||
#else
|
|
||||||
#define R7_OPERAND "r"(r7)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inline long __syscall0(long n)
|
|
||||||
{
|
|
||||||
register long r7 __ASM____R7__ = n;
|
|
||||||
register long r0 __asm__("r0");
|
|
||||||
__asm_syscall(R7_OPERAND);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall1(long n, long a)
|
|
||||||
{
|
|
||||||
register long r7 __ASM____R7__ = n;
|
|
||||||
register long r0 __asm__("r0") = a;
|
|
||||||
__asm_syscall(R7_OPERAND, "0"(r0));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall2(long n, long a, long b)
|
|
||||||
{
|
|
||||||
register long r7 __ASM____R7__ = n;
|
|
||||||
register long r0 __asm__("r0") = a;
|
|
||||||
register long r1 __asm__("r1") = b;
|
|
||||||
__asm_syscall(R7_OPERAND, "0"(r0), "r"(r1));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall3(long n, long a, long b, long c)
|
|
||||||
{
|
|
||||||
register long r7 __ASM____R7__ = n;
|
|
||||||
register long r0 __asm__("r0") = a;
|
|
||||||
register long r1 __asm__("r1") = b;
|
|
||||||
register long r2 __asm__("r2") = c;
|
|
||||||
__asm_syscall(R7_OPERAND, "0"(r0), "r"(r1), "r"(r2));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall4(long n, long a, long b, long c, long d)
|
|
||||||
{
|
|
||||||
register long r7 __ASM____R7__ = n;
|
|
||||||
register long r0 __asm__("r0") = a;
|
|
||||||
register long r1 __asm__("r1") = b;
|
|
||||||
register long r2 __asm__("r2") = c;
|
|
||||||
register long r3 __asm__("r3") = d;
|
|
||||||
__asm_syscall(R7_OPERAND, "0"(r0), "r"(r1), "r"(r2), "r"(r3));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall5(long n, long a, long b, long c, long d, long e)
|
|
||||||
{
|
|
||||||
register long r7 __ASM____R7__ = n;
|
|
||||||
register long r0 __asm__("r0") = a;
|
|
||||||
register long r1 __asm__("r1") = b;
|
|
||||||
register long r2 __asm__("r2") = c;
|
|
||||||
register long r3 __asm__("r3") = d;
|
|
||||||
register long r4 __asm__("r4") = e;
|
|
||||||
__asm_syscall(R7_OPERAND, "0"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long __syscall6(long n, long a, long b, long c, long d, long e, long f)
|
|
||||||
{
|
|
||||||
register long r7 __ASM____R7__ = n;
|
|
||||||
register long r0 __asm__("r0") = a;
|
|
||||||
register long r1 __asm__("r1") = b;
|
|
||||||
register long r2 __asm__("r2") = c;
|
|
||||||
register long r3 __asm__("r3") = d;
|
|
||||||
register long r4 __asm__("r4") = e;
|
|
||||||
register long r5 __asm__("r5") = f;
|
|
||||||
__asm_syscall(R7_OPERAND, "0"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SYSCALL_FADVISE_6_ARG
|
|
||||||
|
|
||||||
#define SYSCALL_IPC_BROKEN_MODE
|
|
|
@ -1,11 +0,0 @@
|
||||||
#define _DIRENT_HAVE_D_RECLEN
|
|
||||||
#define _DIRENT_HAVE_D_OFF
|
|
||||||
#define _DIRENT_HAVE_D_TYPE
|
|
||||||
|
|
||||||
struct dirent {
|
|
||||||
ino_t d_ino;
|
|
||||||
off_t d_off;
|
|
||||||
unsigned short d_reclen;
|
|
||||||
unsigned char d_type;
|
|
||||||
char d_name[256];
|
|
||||||
};
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue