mirror of
https://github.com/ilyakooo0/urbit.git
synced 2025-01-01 19:46:36 +03:00
Merge branch 'king-haskell' of github.com:urbit/urbit into merge-king
This commit is contained in:
commit
c3e52b25e9
18
.travis.yml
18
.travis.yml
@ -1,19 +1,27 @@
|
||||
language: nix
|
||||
nix: 2.1.3
|
||||
|
||||
install:
|
||||
- nix-env -iA cachix -f https://cachix.org/api/v1/install
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.ghc
|
||||
- $HOME/.cabal
|
||||
- $HOME/.stack
|
||||
- $TRAVIS_BUILD_DIR/.stack-work
|
||||
|
||||
before_install:
|
||||
- git lfs pull
|
||||
- sh/travis-install-stack
|
||||
|
||||
install:
|
||||
- nix-env -iA cachix -f https://cachix.org/api/v1/install
|
||||
- stack --no-terminal --install-ghc build king --only-dependencies
|
||||
|
||||
# TODO Only do release builds on the `pull` branch?
|
||||
script:
|
||||
- cachix use urbit2
|
||||
- ./sh/cachix || true
|
||||
|
||||
- make
|
||||
- make build-fast
|
||||
- make release
|
||||
|
||||
- sh/ci-tests
|
||||
|
||||
deploy:
|
||||
|
5
Makefile
5
Makefile
@ -2,6 +2,11 @@
|
||||
|
||||
build:
|
||||
nix-build -A urbit -A herb --no-out-link
|
||||
stack build king
|
||||
|
||||
build-fast:
|
||||
nix-build -A urbit -A herb --no-out-link
|
||||
stack build king --fast
|
||||
|
||||
build-all:
|
||||
nix-build --no-out-link
|
||||
|
49
pkg/king/lib/Noun/Rip.hs
Normal file
49
pkg/king/lib/Noun/Rip.hs
Normal file
@ -0,0 +1,49 @@
|
||||
module Noun.Rip where
|
||||
|
||||
import ClassyPrelude
|
||||
import Noun.Atom
|
||||
import Data.Bits
|
||||
import Control.Lens (view, (&), from)
|
||||
import qualified Data.Vector.Primitive as VP
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
takeBits ∷ Word → Word → Word
|
||||
takeBits 64 w = w
|
||||
takeBits 0 w = 0
|
||||
takeBits n w = w .&. (shiftL 1 (fromIntegral n) - 1)
|
||||
|
||||
divCeil ∷ Word → Word → Word
|
||||
divCeil 0 y = 0
|
||||
divCeil x y = 1 + ((x-1) `div` y)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
repn :: Word -> [Word] -> Atom
|
||||
repn bits blox =
|
||||
(bits > 64) & \case
|
||||
True → error "repn only works with block sizes <= 64"
|
||||
False → view (from atomWords)
|
||||
$ VP.fromList
|
||||
$ finish
|
||||
$ foldl' f ([], 0, 0)
|
||||
$ zip (repeat bits) blox
|
||||
where
|
||||
finish (acc, wor, n) = reverse
|
||||
$ dropWhile (==0)
|
||||
$ case n of { 0 -> acc; _ -> wor:acc }
|
||||
|
||||
slice size off wor = shiftL (takeBits size wor)
|
||||
$ fromIntegral off
|
||||
|
||||
f (acc, wor, off) (remBlok, blok) =
|
||||
let rem = 64 - off in
|
||||
compare remBlok rem & \case
|
||||
LT -> (acc, res, off+bits)
|
||||
where res = wor .|. slice bits off blok
|
||||
EQ -> (res:acc, 0, 0)
|
||||
where res = (wor .|. slice bits off blok)
|
||||
GT -> f (res:acc, 0, 0) (remBlok', blok')
|
||||
where res = wor .|. slice rem off blok
|
||||
remBlok' = remBlok-rem
|
||||
blok' = shiftR blok (fromIntegral bits)
|
@ -1,130 +0,0 @@
|
||||
{-# OPTIONS_GHC -Wwarn #-}
|
||||
|
||||
module Vere.Isle where
|
||||
|
||||
import ClassyPrelude
|
||||
import Data.Word
|
||||
|
||||
import qualified Data.Vector as V
|
||||
import qualified SDL as SDL
|
||||
import qualified Vere.Isle.Util as C
|
||||
|
||||
import Data.Bits (testBit)
|
||||
import Data.Vector ((!))
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
newtype Word4 = Word4 Word8
|
||||
deriving newtype (Eq, Ord, Num, Integral, Real, Enum)
|
||||
|
||||
newtype Word10 = Word10 Word16
|
||||
deriving newtype (Eq, Ord, Num, Integral, Real, Enum)
|
||||
|
||||
data RGB = RGB !Word8 !Word8 !Word8
|
||||
|
||||
type Bitmap = Word64 -- 8x8 bitmap
|
||||
|
||||
{-
|
||||
TODO Storable instance?
|
||||
(Then I can use an unboxed vector)
|
||||
-}
|
||||
data Tile = Tile
|
||||
{ tFore :: !Word4
|
||||
, tBack :: !Word4
|
||||
, tSpry :: !Word10
|
||||
}
|
||||
|
||||
data Display = Display
|
||||
{ dColors :: V.Vector RGB -- size: 16
|
||||
, dSprites :: V.Vector Bitmap -- size: 1024
|
||||
, dTiles :: V.Vector Tile -- size: 3600 (80x45)
|
||||
, dSurf :: V.Vector SDL.Surface -- size: 3600 (80x45)
|
||||
}
|
||||
|
||||
initializeSurfaces :: IO (V.Vector SDL.Surface)
|
||||
initializeSurfaces =
|
||||
V.generateM 3600
|
||||
$ const
|
||||
$ SDL.createRGBSurface (SDL.V2 8 8)
|
||||
$ SDL.RGB888
|
||||
|
||||
initialDisplay :: IO Display
|
||||
initialDisplay =
|
||||
do
|
||||
surf <- initializeSurfaces
|
||||
pure $ Display (V.generate 16 initialColors)
|
||||
(V.generate 1024 initialSprites)
|
||||
(V.generate 3600 initialTiles)
|
||||
surf
|
||||
where
|
||||
initialSprites :: Int -> Bitmap
|
||||
initialSprites = fromIntegral
|
||||
|
||||
green = 4
|
||||
white = 15
|
||||
|
||||
initialTiles :: Int -> Tile
|
||||
initialTiles i =
|
||||
Tile green white (fromIntegral i `mod` 1024)
|
||||
|
||||
initialColors :: Int -> RGB
|
||||
initialColors = \case
|
||||
0 -> RGB 0x00 0x00 0x00 -- Black
|
||||
1 -> RGB 0x55 0x55 0x55 -- DarkGray
|
||||
2 -> RGB 0x00 0x00 0xAA -- Blue
|
||||
3 -> RGB 0x55 0x55 0xFF -- LightBlue
|
||||
4 -> RGB 0x00 0xAA 0x00 -- Green
|
||||
5 -> RGB 0x55 0xFF 0x55 -- LightGreen
|
||||
6 -> RGB 0x00 0xAA 0xAA -- Cyan
|
||||
7 -> RGB 0x55 0xFF 0xFF -- LightCyan
|
||||
8 -> RGB 0xAA 0x00 0x00 -- Red
|
||||
9 -> RGB 0xFF 0x55 0x55 -- LightRed
|
||||
10 -> RGB 0xAA 0x00 0xAA -- Magenta
|
||||
11 -> RGB 0xFF 0x55 0xFF -- LightMagenta
|
||||
12 -> RGB 0xAA 0x55 0x00 -- Brown
|
||||
13 -> RGB 0xFF 0xFF 0x55 -- Yellow
|
||||
14 -> RGB 0xAA 0xAA 0xAA -- LightGray
|
||||
15 -> RGB 0xFF 0xFF 0xFF -- White
|
||||
n -> error ("bad color: " <> show n)
|
||||
|
||||
renderTile :: Display -> Tile -> SDL.Surface -> IO ()
|
||||
renderTile d (Tile fg bg tx) surf = do
|
||||
let for = dColors d ! fromIntegral fg
|
||||
let bac = dColors d ! fromIntegral bg
|
||||
let spry = dSprites d ! fromIntegral tx
|
||||
for_ [0..63] $ \i -> do
|
||||
let col = if testBit spry i then for else bac
|
||||
renderPixel i surf col
|
||||
|
||||
renderPixel :: Int -> SDL.Surface -> RGB -> IO ()
|
||||
renderPixel = undefined
|
||||
|
||||
|
||||
-- data Display = Display
|
||||
{-dColors :: V.Vector RGB -- size: 16
|
||||
, dSprites :: V.Vector Bitmap -- size: 1024
|
||||
, dTiles :: V.Vector Tile -- size: 3600 (80x45)
|
||||
, dSurf :: V.Vector SDL.Surface -- size: 3600 (80x45)
|
||||
-}
|
||||
|
||||
render :: Display -> IO ()
|
||||
render = undefined
|
||||
|
||||
draw :: Display -> IO ()
|
||||
draw = undefined
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
main :: IO ()
|
||||
main = C.withSDL $ C.withWindow "Lesson 01" (640, 480) $
|
||||
\w -> do
|
||||
|
||||
screen <- SDL.getWindowSurface w
|
||||
-- pixelFormat <- SDL.surfaceFormat `applyToPointer` screen
|
||||
-- color <- SDL.mapRGB pixelFormat 0xFF 0xFF 0xFF
|
||||
SDL.surfaceFillRect screen Nothing (SDL.V4 maxBound maxBound maxBound maxBound)
|
||||
SDL.updateWindowSurface w
|
||||
|
||||
SDL.delay 2000
|
||||
|
||||
SDL.freeSurface screen
|
@ -1,94 +0,0 @@
|
||||
module Vere.Isle.Util where
|
||||
|
||||
import Prelude
|
||||
|
||||
import Control.Monad (void)
|
||||
import Control.Monad.IO.Class (MonadIO)
|
||||
import Data.Text (Text)
|
||||
import SDL (($=))
|
||||
|
||||
import qualified SDL
|
||||
import qualified SDL.Image
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
withSDL :: (MonadIO m) => m a -> m ()
|
||||
withSDL op = do
|
||||
SDL.initialize []
|
||||
void op
|
||||
SDL.quit
|
||||
|
||||
|
||||
withSDLImage :: (MonadIO m) => m a -> m ()
|
||||
withSDLImage op = do
|
||||
SDL.Image.initialize []
|
||||
void op
|
||||
SDL.Image.quit
|
||||
|
||||
|
||||
withWindow :: (MonadIO m) => Text -> (Int, Int) -> (SDL.Window -> m a) -> m ()
|
||||
withWindow title (x, y) op = do
|
||||
w <- SDL.createWindow title p
|
||||
SDL.showWindow w
|
||||
void $ op w
|
||||
SDL.destroyWindow w
|
||||
|
||||
where
|
||||
p = SDL.defaultWindow { SDL.windowInitialSize = z }
|
||||
z = SDL.V2 (fromIntegral x) (fromIntegral y)
|
||||
|
||||
|
||||
withRenderer :: (MonadIO m) => SDL.Window -> (SDL.Renderer -> m a) -> m ()
|
||||
withRenderer w op = do
|
||||
r <- SDL.createRenderer w (-1) rendererConfig
|
||||
void $ op r
|
||||
SDL.destroyRenderer r
|
||||
|
||||
|
||||
rendererConfig :: SDL.RendererConfig
|
||||
rendererConfig = SDL.RendererConfig
|
||||
{ SDL.rendererType = SDL.AcceleratedVSyncRenderer
|
||||
, SDL.rendererTargetTexture = False
|
||||
}
|
||||
|
||||
|
||||
renderSurfaceToWindow :: (MonadIO m) => SDL.Window -> SDL.Surface -> SDL.Surface -> m ()
|
||||
renderSurfaceToWindow w s i
|
||||
= SDL.surfaceBlit i Nothing s Nothing
|
||||
>> SDL.updateWindowSurface w
|
||||
|
||||
|
||||
isContinue :: Maybe SDL.Event -> Bool
|
||||
isContinue = maybe True (not . isQuitEvent)
|
||||
|
||||
|
||||
conditionallyRun :: (Monad m) => m a -> Bool -> m Bool
|
||||
conditionallyRun f True = True <$ f
|
||||
conditionallyRun _ False = pure False
|
||||
|
||||
|
||||
isQuitEvent :: SDL.Event -> Bool
|
||||
isQuitEvent (SDL.Event _t SDL.QuitEvent) = True
|
||||
isQuitEvent _ = False
|
||||
|
||||
|
||||
setHintQuality :: (MonadIO m) => m ()
|
||||
setHintQuality = SDL.HintRenderScaleQuality $= SDL.ScaleNearest
|
||||
|
||||
|
||||
loadTextureWithInfo :: (MonadIO m) => SDL.Renderer -> FilePath -> m (SDL.Texture, SDL.TextureInfo)
|
||||
loadTextureWithInfo r p = do
|
||||
t <- SDL.Image.loadTexture r p
|
||||
i <- SDL.queryTexture t
|
||||
pure (t, i)
|
||||
|
||||
|
||||
mkPoint :: a -> a -> SDL.Point SDL.V2 a
|
||||
mkPoint x y = SDL.P (SDL.V2 x y)
|
||||
|
||||
|
||||
mkRect :: a -> a -> a -> a-> SDL.Rectangle a
|
||||
mkRect x y w h = SDL.Rectangle o z
|
||||
where
|
||||
o = SDL.P (SDL.V2 x y)
|
||||
z = SDL.V2 w h
|
@ -60,7 +60,7 @@ dependencies:
|
||||
- iproute
|
||||
- largeword
|
||||
- lens
|
||||
- lmdb
|
||||
- lmdb-static
|
||||
- lock-file
|
||||
- megaparsec
|
||||
- memory
|
||||
@ -79,8 +79,6 @@ dependencies:
|
||||
- regex-tdfa-text
|
||||
- resourcet
|
||||
- rio
|
||||
- sdl2
|
||||
- sdl2-image
|
||||
- semigroups
|
||||
- smallcheck
|
||||
- stm
|
||||
|
@ -123,9 +123,11 @@ tests =
|
||||
[ localOption (QuickCheckTests 10) $
|
||||
testProperty "Zod can send a message to itself" $
|
||||
zodSelfMsg
|
||||
, localOption (QuickCheckTests 10) $
|
||||
testProperty "Two galaxies can talk" $
|
||||
twoTalk
|
||||
|
||||
-- TODO Why doesn't this work in CI?
|
||||
-- , localOption (QuickCheckTests 10) $
|
||||
-- testProperty "Two galaxies can talk" $
|
||||
-- twoTalk
|
||||
]
|
||||
|
||||
|
||||
|
12
pkg/lmdb-static/.gitignore
vendored
Normal file
12
pkg/lmdb-static/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
dist
|
||||
cabal-dev
|
||||
*.o
|
||||
*.hi
|
||||
*.chi
|
||||
*.chs.h
|
||||
.virtualenv
|
||||
.hsenv
|
||||
.cabal-sandbox/
|
||||
cabal.sandbox.config
|
||||
cabal.config
|
||||
*~
|
24
pkg/lmdb-static/LICENSE
Normal file
24
pkg/lmdb-static/LICENSE
Normal file
@ -0,0 +1,24 @@
|
||||
Copyright (c) 2014, David Barbour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
|
||||
|
13
pkg/lmdb-static/README.md
Normal file
13
pkg/lmdb-static/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
This is a hack to avoid dynamic depencency on lmdb:
|
||||
|
||||
This is a vendoring of `haskell-lmdb` and `lmdb` modified to include
|
||||
the c-build of `lmdb` statically into `haskell-lmdb`.
|
||||
|
||||
```
|
||||
haskell-lmdb:
|
||||
repo: https://github.com/dmbarbour/haskell-lmdb.git
|
||||
hash: 1e562429874919d445576c87cf118d7de5112b5b
|
||||
lmdb:
|
||||
repo: https://github.com/LMDB/lmdb.git
|
||||
hash: c3e6b4209eed13af4a3670e5f04f42169c08e5c6
|
||||
```
|
3
pkg/lmdb-static/Setup.hs
Normal file
3
pkg/lmdb-static/Setup.hs
Normal file
@ -0,0 +1,3 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
||||
|
1653
pkg/lmdb-static/cbits/lmdb.h
Normal file
1653
pkg/lmdb-static/cbits/lmdb.h
Normal file
File diff suppressed because it is too large
Load Diff
11199
pkg/lmdb-static/cbits/mdb.c
Normal file
11199
pkg/lmdb-static/cbits/mdb.c
Normal file
File diff suppressed because it is too large
Load Diff
421
pkg/lmdb-static/cbits/midl.c
Normal file
421
pkg/lmdb-static/cbits/midl.c
Normal file
@ -0,0 +1,421 @@
|
||||
/** @file midl.c
|
||||
* @brief ldap bdb back-end ID List functions */
|
||||
/* $OpenLDAP$ */
|
||||
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
*
|
||||
* Copyright 2000-2019 The OpenLDAP Foundation.
|
||||
* Portions Copyright 2001-2018 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include "midl.h"
|
||||
|
||||
/** @defgroup internal LMDB Internals
|
||||
* @{
|
||||
*/
|
||||
/** @defgroup idls ID List Management
|
||||
* @{
|
||||
*/
|
||||
#define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) )
|
||||
|
||||
unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id )
|
||||
{
|
||||
/*
|
||||
* binary search of id in ids
|
||||
* if found, returns position of id
|
||||
* if not found, returns first position greater than id
|
||||
*/
|
||||
unsigned base = 0;
|
||||
unsigned cursor = 1;
|
||||
int val = 0;
|
||||
unsigned n = ids[0];
|
||||
|
||||
while( 0 < n ) {
|
||||
unsigned pivot = n >> 1;
|
||||
cursor = base + pivot + 1;
|
||||
val = CMP( ids[cursor], id );
|
||||
|
||||
if( val < 0 ) {
|
||||
n = pivot;
|
||||
|
||||
} else if ( val > 0 ) {
|
||||
base = cursor;
|
||||
n -= pivot + 1;
|
||||
|
||||
} else {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if( val > 0 ) {
|
||||
++cursor;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
#if 0 /* superseded by append/sort */
|
||||
int mdb_midl_insert( MDB_IDL ids, MDB_ID id )
|
||||
{
|
||||
unsigned x, i;
|
||||
|
||||
x = mdb_midl_search( ids, id );
|
||||
assert( x > 0 );
|
||||
|
||||
if( x < 1 ) {
|
||||
/* internal error */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ( x <= ids[0] && ids[x] == id ) {
|
||||
/* duplicate */
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ++ids[0] >= MDB_IDL_DB_MAX ) {
|
||||
/* no room */
|
||||
--ids[0];
|
||||
return -2;
|
||||
|
||||
} else {
|
||||
/* insert id */
|
||||
for (i=ids[0]; i>x; i--)
|
||||
ids[i] = ids[i-1];
|
||||
ids[x] = id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
MDB_IDL mdb_midl_alloc(int num)
|
||||
{
|
||||
MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID));
|
||||
if (ids) {
|
||||
*ids++ = num;
|
||||
*ids = 0;
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
void mdb_midl_free(MDB_IDL ids)
|
||||
{
|
||||
if (ids)
|
||||
free(ids-1);
|
||||
}
|
||||
|
||||
void mdb_midl_shrink( MDB_IDL *idp )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
if (*(--ids) > MDB_IDL_UM_MAX &&
|
||||
(ids = realloc(ids, (MDB_IDL_UM_MAX+2) * sizeof(MDB_ID))))
|
||||
{
|
||||
*ids++ = MDB_IDL_UM_MAX;
|
||||
*idp = ids;
|
||||
}
|
||||
}
|
||||
|
||||
static int mdb_midl_grow( MDB_IDL *idp, int num )
|
||||
{
|
||||
MDB_IDL idn = *idp-1;
|
||||
/* grow it */
|
||||
idn = realloc(idn, (*idn + num + 2) * sizeof(MDB_ID));
|
||||
if (!idn)
|
||||
return ENOMEM;
|
||||
*idn++ += num;
|
||||
*idp = idn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_need( MDB_IDL *idp, unsigned num )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
num += ids[0];
|
||||
if (num > ids[-1]) {
|
||||
num = (num + num/4 + (256 + 2)) & -256;
|
||||
if (!(ids = realloc(ids-1, num * sizeof(MDB_ID))))
|
||||
return ENOMEM;
|
||||
*ids++ = num - 2;
|
||||
*idp = ids;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append( MDB_IDL *idp, MDB_ID id )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
/* Too big? */
|
||||
if (ids[0] >= ids[-1]) {
|
||||
if (mdb_midl_grow(idp, MDB_IDL_UM_MAX))
|
||||
return ENOMEM;
|
||||
ids = *idp;
|
||||
}
|
||||
ids[0]++;
|
||||
ids[ids[0]] = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
/* Too big? */
|
||||
if (ids[0] + app[0] >= ids[-1]) {
|
||||
if (mdb_midl_grow(idp, app[0]))
|
||||
return ENOMEM;
|
||||
ids = *idp;
|
||||
}
|
||||
memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID));
|
||||
ids[0] += app[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n )
|
||||
{
|
||||
MDB_ID *ids = *idp, len = ids[0];
|
||||
/* Too big? */
|
||||
if (len + n > ids[-1]) {
|
||||
if (mdb_midl_grow(idp, n | MDB_IDL_UM_MAX))
|
||||
return ENOMEM;
|
||||
ids = *idp;
|
||||
}
|
||||
ids[0] = len + n;
|
||||
ids += len;
|
||||
while (n)
|
||||
ids[n--] = id++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge )
|
||||
{
|
||||
MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k;
|
||||
idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */
|
||||
old_id = idl[j];
|
||||
while (i) {
|
||||
merge_id = merge[i--];
|
||||
for (; old_id < merge_id; old_id = idl[--j])
|
||||
idl[k--] = old_id;
|
||||
idl[k--] = merge_id;
|
||||
}
|
||||
idl[0] = total;
|
||||
}
|
||||
|
||||
/* Quicksort + Insertion sort for small arrays */
|
||||
|
||||
#define SMALL 8
|
||||
#define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; }
|
||||
|
||||
void
|
||||
mdb_midl_sort( MDB_IDL ids )
|
||||
{
|
||||
/* Max possible depth of int-indexed tree * 2 items/level */
|
||||
int istack[sizeof(int)*CHAR_BIT * 2];
|
||||
int i,j,k,l,ir,jstack;
|
||||
MDB_ID a, itmp;
|
||||
|
||||
ir = (int)ids[0];
|
||||
l = 1;
|
||||
jstack = 0;
|
||||
for(;;) {
|
||||
if (ir - l < SMALL) { /* Insertion sort */
|
||||
for (j=l+1;j<=ir;j++) {
|
||||
a = ids[j];
|
||||
for (i=j-1;i>=1;i--) {
|
||||
if (ids[i] >= a) break;
|
||||
ids[i+1] = ids[i];
|
||||
}
|
||||
ids[i+1] = a;
|
||||
}
|
||||
if (jstack == 0) break;
|
||||
ir = istack[jstack--];
|
||||
l = istack[jstack--];
|
||||
} else {
|
||||
k = (l + ir) >> 1; /* Choose median of left, center, right */
|
||||
MIDL_SWAP(ids[k], ids[l+1]);
|
||||
if (ids[l] < ids[ir]) {
|
||||
MIDL_SWAP(ids[l], ids[ir]);
|
||||
}
|
||||
if (ids[l+1] < ids[ir]) {
|
||||
MIDL_SWAP(ids[l+1], ids[ir]);
|
||||
}
|
||||
if (ids[l] < ids[l+1]) {
|
||||
MIDL_SWAP(ids[l], ids[l+1]);
|
||||
}
|
||||
i = l+1;
|
||||
j = ir;
|
||||
a = ids[l+1];
|
||||
for(;;) {
|
||||
do i++; while(ids[i] > a);
|
||||
do j--; while(ids[j] < a);
|
||||
if (j < i) break;
|
||||
MIDL_SWAP(ids[i],ids[j]);
|
||||
}
|
||||
ids[l+1] = ids[j];
|
||||
ids[j] = a;
|
||||
jstack += 2;
|
||||
if (ir-i+1 >= j-l) {
|
||||
istack[jstack] = ir;
|
||||
istack[jstack-1] = i;
|
||||
ir = j-1;
|
||||
} else {
|
||||
istack[jstack] = j-1;
|
||||
istack[jstack-1] = l;
|
||||
l = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id )
|
||||
{
|
||||
/*
|
||||
* binary search of id in ids
|
||||
* if found, returns position of id
|
||||
* if not found, returns first position greater than id
|
||||
*/
|
||||
unsigned base = 0;
|
||||
unsigned cursor = 1;
|
||||
int val = 0;
|
||||
unsigned n = (unsigned)ids[0].mid;
|
||||
|
||||
while( 0 < n ) {
|
||||
unsigned pivot = n >> 1;
|
||||
cursor = base + pivot + 1;
|
||||
val = CMP( id, ids[cursor].mid );
|
||||
|
||||
if( val < 0 ) {
|
||||
n = pivot;
|
||||
|
||||
} else if ( val > 0 ) {
|
||||
base = cursor;
|
||||
n -= pivot + 1;
|
||||
|
||||
} else {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if( val > 0 ) {
|
||||
++cursor;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id )
|
||||
{
|
||||
unsigned x, i;
|
||||
|
||||
x = mdb_mid2l_search( ids, id->mid );
|
||||
|
||||
if( x < 1 ) {
|
||||
/* internal error */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ( x <= ids[0].mid && ids[x].mid == id->mid ) {
|
||||
/* duplicate */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ids[0].mid >= MDB_IDL_UM_MAX ) {
|
||||
/* too big */
|
||||
return -2;
|
||||
|
||||
} else {
|
||||
/* insert id */
|
||||
ids[0].mid++;
|
||||
for (i=(unsigned)ids[0].mid; i>x; i--)
|
||||
ids[i] = ids[i-1];
|
||||
ids[x] = *id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id )
|
||||
{
|
||||
/* Too big? */
|
||||
if (ids[0].mid >= MDB_IDL_UM_MAX) {
|
||||
return -2;
|
||||
}
|
||||
ids[0].mid++;
|
||||
ids[ids[0].mid] = *id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef MDB_VL32
|
||||
unsigned mdb_mid3l_search( MDB_ID3L ids, MDB_ID id )
|
||||
{
|
||||
/*
|
||||
* binary search of id in ids
|
||||
* if found, returns position of id
|
||||
* if not found, returns first position greater than id
|
||||
*/
|
||||
unsigned base = 0;
|
||||
unsigned cursor = 1;
|
||||
int val = 0;
|
||||
unsigned n = (unsigned)ids[0].mid;
|
||||
|
||||
while( 0 < n ) {
|
||||
unsigned pivot = n >> 1;
|
||||
cursor = base + pivot + 1;
|
||||
val = CMP( id, ids[cursor].mid );
|
||||
|
||||
if( val < 0 ) {
|
||||
n = pivot;
|
||||
|
||||
} else if ( val > 0 ) {
|
||||
base = cursor;
|
||||
n -= pivot + 1;
|
||||
|
||||
} else {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if( val > 0 ) {
|
||||
++cursor;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
int mdb_mid3l_insert( MDB_ID3L ids, MDB_ID3 *id )
|
||||
{
|
||||
unsigned x, i;
|
||||
|
||||
x = mdb_mid3l_search( ids, id->mid );
|
||||
|
||||
if( x < 1 ) {
|
||||
/* internal error */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ( x <= ids[0].mid && ids[x].mid == id->mid ) {
|
||||
/* duplicate */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* insert id */
|
||||
ids[0].mid++;
|
||||
for (i=(unsigned)ids[0].mid; i>x; i--)
|
||||
ids[i] = ids[i-1];
|
||||
ids[x] = *id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* MDB_VL32 */
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
200
pkg/lmdb-static/cbits/midl.h
Normal file
200
pkg/lmdb-static/cbits/midl.h
Normal file
@ -0,0 +1,200 @@
|
||||
/** @file midl.h
|
||||
* @brief LMDB ID List header file.
|
||||
*
|
||||
* This file was originally part of back-bdb but has been
|
||||
* modified for use in libmdb. Most of the macros defined
|
||||
* in this file are unused, just left over from the original.
|
||||
*
|
||||
* This file is only used internally in libmdb and its definitions
|
||||
* are not exposed publicly.
|
||||
*/
|
||||
/* $OpenLDAP$ */
|
||||
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
*
|
||||
* Copyright 2000-2019 The OpenLDAP Foundation.
|
||||
* Portions Copyright 2001-2019 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#ifndef _MDB_MIDL_H_
|
||||
#define _MDB_MIDL_H_
|
||||
|
||||
#include "lmdb.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup internal LMDB Internals
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup idls ID List Management
|
||||
* @{
|
||||
*/
|
||||
/** A generic unsigned ID number. These were entryIDs in back-bdb.
|
||||
* Preferably it should have the same size as a pointer.
|
||||
*/
|
||||
typedef mdb_size_t MDB_ID;
|
||||
|
||||
/** An IDL is an ID List, a sorted array of IDs. The first
|
||||
* element of the array is a counter for how many actual
|
||||
* IDs are in the list. In the original back-bdb code, IDLs are
|
||||
* sorted in ascending order. For libmdb IDLs are sorted in
|
||||
* descending order.
|
||||
*/
|
||||
typedef MDB_ID *MDB_IDL;
|
||||
|
||||
/* IDL sizes - likely should be even bigger
|
||||
* limiting factors: sizeof(ID), thread stack size
|
||||
*/
|
||||
#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
|
||||
#define MDB_IDL_DB_SIZE (1<<MDB_IDL_LOGN)
|
||||
#define MDB_IDL_UM_SIZE (1<<(MDB_IDL_LOGN+1))
|
||||
|
||||
#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE-1)
|
||||
#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE-1)
|
||||
|
||||
#define MDB_IDL_SIZEOF(ids) (((ids)[0]+1) * sizeof(MDB_ID))
|
||||
#define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
|
||||
#define MDB_IDL_CPY( dst, src ) (memcpy( dst, src, MDB_IDL_SIZEOF( src ) ))
|
||||
#define MDB_IDL_FIRST( ids ) ( (ids)[1] )
|
||||
#define MDB_IDL_LAST( ids ) ( (ids)[(ids)[0]] )
|
||||
|
||||
/** Current max length of an #mdb_midl_alloc()ed IDL */
|
||||
#define MDB_IDL_ALLOCLEN( ids ) ( (ids)[-1] )
|
||||
|
||||
/** Append ID to IDL. The IDL must be big enough. */
|
||||
#define mdb_midl_xappend(idl, id) do { \
|
||||
MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \
|
||||
xidl[xlen] = (id); \
|
||||
} while (0)
|
||||
|
||||
/** Search for an ID in an IDL.
|
||||
* @param[in] ids The IDL to search.
|
||||
* @param[in] id The ID to search for.
|
||||
* @return The index of the first ID greater than or equal to \b id.
|
||||
*/
|
||||
unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id );
|
||||
|
||||
/** Allocate an IDL.
|
||||
* Allocates memory for an IDL of the given size.
|
||||
* @return IDL on success, NULL on failure.
|
||||
*/
|
||||
MDB_IDL mdb_midl_alloc(int num);
|
||||
|
||||
/** Free an IDL.
|
||||
* @param[in] ids The IDL to free.
|
||||
*/
|
||||
void mdb_midl_free(MDB_IDL ids);
|
||||
|
||||
/** Shrink an IDL.
|
||||
* Return the IDL to the default size if it has grown larger.
|
||||
* @param[in,out] idp Address of the IDL to shrink.
|
||||
*/
|
||||
void mdb_midl_shrink(MDB_IDL *idp);
|
||||
|
||||
/** Make room for num additional elements in an IDL.
|
||||
* @param[in,out] idp Address of the IDL.
|
||||
* @param[in] num Number of elements to make room for.
|
||||
* @return 0 on success, ENOMEM on failure.
|
||||
*/
|
||||
int mdb_midl_need(MDB_IDL *idp, unsigned num);
|
||||
|
||||
/** Append an ID onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] id The ID to append.
|
||||
* @return 0 on success, ENOMEM if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append( MDB_IDL *idp, MDB_ID id );
|
||||
|
||||
/** Append an IDL onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] app The IDL to append.
|
||||
* @return 0 on success, ENOMEM if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app );
|
||||
|
||||
/** Append an ID range onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] id The lowest ID to append.
|
||||
* @param[in] n Number of IDs to append.
|
||||
* @return 0 on success, ENOMEM if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n );
|
||||
|
||||
/** Merge an IDL onto an IDL. The destination IDL must be big enough.
|
||||
* @param[in] idl The IDL to merge into.
|
||||
* @param[in] merge The IDL to merge.
|
||||
*/
|
||||
void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge );
|
||||
|
||||
/** Sort an IDL.
|
||||
* @param[in,out] ids The IDL to sort.
|
||||
*/
|
||||
void mdb_midl_sort( MDB_IDL ids );
|
||||
|
||||
/** An ID2 is an ID/pointer pair.
|
||||
*/
|
||||
typedef struct MDB_ID2 {
|
||||
MDB_ID mid; /**< The ID */
|
||||
void *mptr; /**< The pointer */
|
||||
} MDB_ID2;
|
||||
|
||||
/** An ID2L is an ID2 List, a sorted array of ID2s.
|
||||
* The first element's \b mid member is a count of how many actual
|
||||
* elements are in the array. The \b mptr member of the first element is unused.
|
||||
* The array is sorted in ascending order by \b mid.
|
||||
*/
|
||||
typedef MDB_ID2 *MDB_ID2L;
|
||||
|
||||
/** Search for an ID in an ID2L.
|
||||
* @param[in] ids The ID2L to search.
|
||||
* @param[in] id The ID to search for.
|
||||
* @return The index of the first ID2 whose \b mid member is greater than or equal to \b id.
|
||||
*/
|
||||
unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id );
|
||||
|
||||
|
||||
/** Insert an ID2 into a ID2L.
|
||||
* @param[in,out] ids The ID2L to insert into.
|
||||
* @param[in] id The ID2 to insert.
|
||||
* @return 0 on success, -1 if the ID was already present in the ID2L.
|
||||
*/
|
||||
int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id );
|
||||
|
||||
/** Append an ID2 into a ID2L.
|
||||
* @param[in,out] ids The ID2L to append into.
|
||||
* @param[in] id The ID2 to append.
|
||||
* @return 0 on success, -2 if the ID2L is too big.
|
||||
*/
|
||||
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id );
|
||||
|
||||
#ifdef MDB_VL32
|
||||
typedef struct MDB_ID3 {
|
||||
MDB_ID mid; /**< The ID */
|
||||
void *mptr; /**< The pointer */
|
||||
unsigned int mcnt; /**< Number of pages */
|
||||
unsigned int mref; /**< Refcounter */
|
||||
} MDB_ID3;
|
||||
|
||||
typedef MDB_ID3 *MDB_ID3L;
|
||||
|
||||
unsigned mdb_mid3l_search( MDB_ID3L ids, MDB_ID id );
|
||||
int mdb_mid3l_insert( MDB_ID3L ids, MDB_ID3 *id );
|
||||
|
||||
#endif /* MDB_VL32 */
|
||||
/** @} */
|
||||
/** @} */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _MDB_MIDL_H_ */
|
1443
pkg/lmdb-static/hsrc_lib/Database/LMDB/Raw.hsc
Normal file
1443
pkg/lmdb-static/hsrc_lib/Database/LMDB/Raw.hsc
Normal file
File diff suppressed because it is too large
Load Diff
47
pkg/lmdb-static/lmdb-static.cabal
Normal file
47
pkg/lmdb-static/lmdb-static.cabal
Normal file
@ -0,0 +1,47 @@
|
||||
Name: lmdb-static
|
||||
Version: 0.2.5
|
||||
Synopsis: Lightning MDB bindings
|
||||
Category: Database
|
||||
Description:
|
||||
LMDB is a read-optimized Berkeley DB replacement developed by Symas
|
||||
for the OpenLDAP project. LMDB has impressive performance characteristics
|
||||
and a friendly BSD-style OpenLDAP license. See <http://symas.com/mdb/>.
|
||||
.
|
||||
This library has Haskell bindings to the LMDB library. You must install
|
||||
the lmdb development files before installing this library,
|
||||
e.g. `sudo apt-get install liblmdb-dev` works for Ubuntu 14.04.
|
||||
.
|
||||
For now, only a low level interface is provided, and the author is moving
|
||||
on to use LMDB rather than further develop its bindings. If a higher level
|
||||
API is desired, please consider contributing, or develop a separate package.
|
||||
|
||||
Author: David Barbour
|
||||
Maintainer: dmbarbour@gmail.com
|
||||
Homepage: http://github.com/dmbarbour/haskell-lmdb
|
||||
|
||||
Package-Url:
|
||||
Copyright: (c) 2014 by David Barbour
|
||||
License: BSD2
|
||||
license-file: LICENSE
|
||||
Stability: experimental
|
||||
build-type: Simple
|
||||
cabal-version: >= 1.16.0.3
|
||||
|
||||
Source-repository head
|
||||
type: git
|
||||
location: http://github.com/dmbarbour/haskell-lmdb.git
|
||||
|
||||
Library
|
||||
hs-Source-Dirs: hsrc_lib
|
||||
default-language: Haskell2010
|
||||
Build-Depends: base (>= 4.6 && < 5), array
|
||||
Build-Tools: hsc2hs
|
||||
|
||||
Exposed-Modules:
|
||||
Database.LMDB.Raw
|
||||
|
||||
Include-dirs: cbits
|
||||
Includes: lmdb.h midl.h
|
||||
C-Sources: cbits/mdb.c cbits/midl.c
|
||||
cc-options: -Wall -O2 -g -pthread
|
||||
ghc-options: -Wall -auto-all
|
@ -2,6 +2,8 @@
|
||||
|
||||
set -ex
|
||||
|
||||
stack test king --fast
|
||||
|
||||
if [ "$TRAVIS_PULL_REQUEST" = false ]
|
||||
then
|
||||
|
||||
|
@ -14,6 +14,13 @@ fi
|
||||
|
||||
mkdir -p release
|
||||
|
||||
stack clean # Make sure we optimize
|
||||
stack install king --local-bin-path ./release
|
||||
mv release/king release/king-linux64-$ver
|
||||
|
||||
# OSX
|
||||
# otool -L ./release/king-darwin-$ver
|
||||
|
||||
for plat in linux64 darwin
|
||||
do
|
||||
sh/cross urbit "$plat"
|
||||
|
2
sh/test
2
sh/test
@ -2,6 +2,8 @@
|
||||
|
||||
set -e
|
||||
|
||||
stack test king --fast
|
||||
|
||||
pkg=$(nix-build nix/ops -A test --no-out-link "$@")
|
||||
|
||||
hdr () {
|
||||
|
24
sh/travis-install-stack
Executable file
24
sh/travis-install-stack
Executable file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
mkdir -p ~/.local/bin
|
||||
|
||||
# OSX
|
||||
# curl -L https://get.haskellstack.org/stable/osx-x86_64.tar.gz | tar xz --strip-components=1 --include '*/stack' -C ~/.local/bin
|
||||
|
||||
mkdir -p tmp
|
||||
|
||||
pax="https://get.haskellstack.org/stable/linux-x86_64.tar.gz"
|
||||
|
||||
curl -L $pax | tar xz -C tmp
|
||||
|
||||
mv tmp/stack-*/stack ~/.local/bin
|
||||
|
||||
# Use the more reliable S3 mirror of Hackage
|
||||
mkdir -p ~/.cabal
|
||||
|
||||
cat > ~/.cabal/config <<EOF
|
||||
remote-repo: hackage.haskell.org:http://hackage.fpcomplete.com/
|
||||
remote-repo-cache: $HOME/.cabal/packages
|
||||
EOF
|
@ -2,6 +2,7 @@ resolver: lts-14.4
|
||||
|
||||
packages:
|
||||
- pkg/king
|
||||
- pkg/lmdb-static
|
||||
|
||||
extra-deps:
|
||||
- para-1.1@sha256:a90eebb063ad70271e6e2a7f00a93e8e8f8b77273f100f39852fbf8301926f81
|
||||
@ -13,13 +14,8 @@ extra-deps:
|
||||
nix:
|
||||
packages:
|
||||
- pkgconfig
|
||||
- SDL2
|
||||
- SDL2_image
|
||||
- zlib
|
||||
|
||||
ghc-options:
|
||||
king: -fobject-code
|
||||
|
||||
flags:
|
||||
king:
|
||||
Release: false
|
||||
|
Loading…
Reference in New Issue
Block a user