Skip to content

Commit

Permalink
Merge pull request #438 from kitsuyui/biticon
Browse files Browse the repository at this point in the history
Implement BitIcon and UUIDIcon
  • Loading branch information
kitsuyui authored Jun 10, 2024
2 parents 7b5ed23 + 1852485 commit 2c55242
Show file tree
Hide file tree
Showing 16 changed files with 299 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[{packages,src,scripts}/**.{ts,json,js,css,tsx,jsx}]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
treemap,
editablelabel,
tab,
biticon,
components,
style-bulma,
]
Expand Down
1 change: 1 addition & 0 deletions examples/storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"private": true,
"devDependencies": {
"@kitsuyui/react-binary": "workspace:^",
"@kitsuyui/react-biticon": "workspace:^",
"@kitsuyui/react-clock": "workspace:^",
"@kitsuyui/react-components": "workspace:^",
"@kitsuyui/react-dekamoji": "workspace:^",
Expand Down
17 changes: 17 additions & 0 deletions examples/storybook/stories/biticon/Detailed.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { UUIDIcon } from '@kitsuyui/react-biticon'

import type { Meta, StoryObj } from '@storybook/react'

const meta: Meta<typeof UUIDIcon> = {
title: 'Base/Biticon/Detailed',
component: UUIDIcon,
}

export default meta
type Story = StoryObj<typeof UUIDIcon>

export const Default: Story = {
args: {
uuid: '018fc860-4d10-7b70-b012-9ca7c3525a13',
},
}
15 changes: 15 additions & 0 deletions examples/storybook/stories/biticon/Introduction.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# @kitsuyui/react-biticon

import { Canvas, Meta } from '@storybook/blocks'

import * as DetailedStories from './Detailed.stories'
import { BitIcon } from "@kitsuyui/react-biticon";

<Meta title="Base/Biticon/Introduction" />
<Canvas of={DetailedStories.Default} />

As you can see, this package is a React component that displays 128-bit data as an icon.

## Installation

## Usage
17 changes: 17 additions & 0 deletions examples/storybook/stories/biticon/Simple.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { UUIDIcon } from '@kitsuyui/react-biticon'

import type { Meta, StoryObj } from '@storybook/react'

const meta: Meta<typeof UUIDIcon> = {
title: 'Base/Biticon/Simple',
component: UUIDIcon,
}

export default meta
type Story = StoryObj<typeof UUIDIcon>

export const Default: Story = {
args: {
uuid: '018fc860-4d10-7b70-b012-9ca7c3525a13',
},
}
4 changes: 3 additions & 1 deletion examples/storybook/stories/example.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ export default meta
type Story = StoryObj<typeof Something>

export const Default: Story = {
args: {},
args: {
bits: 0b10101010,
},
}
4 changes: 4 additions & 0 deletions packages/biticon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# @kitsuyui/react-biticon

Bit icon component for React.
This package is a React component that displays 128-bit data as an icon.
35 changes: 35 additions & 0 deletions packages/biticon/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "@kitsuyui/react-biticon",
"version": "0.0.0",
"description": "",
"license": "MIT",
"author": "Yui Kitsu <kitsuyui@kitsuyui.com>",
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
},
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist",
"package.json"
],
"scripts": {
"build": "tsup --config ../../tsup.config.mjs --clean",
"dev": "tsup --config ../../tsup.config.mjs --watch"
},
"devDependencies": {
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
}
}
84 changes: 84 additions & 0 deletions packages/biticon/src/base.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react'
import { COLORS } from './colors'
import { applySBox128bitBigintTo128bitBigint } from './bitShuffle'


export const UUIDIcon = (props: { uuid: string }): JSX.Element => {
const uuid = props.uuid
if (!isValidUUID(uuid)) {
return <span>Invalid UUID</span>
}
const bits = uuidToBigInt(uuid)
const newBits = applySBox128bitBigintTo128bitBigint(bits)
return <BitIcon bits={newBits} />
}

const isValidUUID = (uuid: string): boolean => {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(uuid)
}

const uuidToBigInt = (uuid: string): bigint => {
// strip if it has dashes
const uuid_ = uuid.replace(/-/g, '')
return BigInt(`0x${uuid_}`)
}


export const BitIcon = (props: { bits: bigint }): JSX.Element => {
const bits = props.bits
if (!is128Bit(bits)) {
return (<span>Invalid 128bit value</span>)
}
const items = from128bitTo8bitArray(bits)
const width = 64
const height = 64
const numOfItems = items.length
const numOfRows = Math.ceil(Math.sqrt(numOfItems))
const numOfCols = Math.ceil(numOfItems / numOfRows)
const itemWidth = width / numOfCols
const itemHeight = height / numOfRows
const text = items.reduce((acc, item) => acc + item.toString(16), '')

return <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} xmlns="http://www.w3.org/2000/svg" aria-label={`BitIcon: ${text}`} role="img">
{Array.from(items).map((item: number, index: number) => {
const indexOfX = index % numOfCols // 0-7
const indexOfY = Math.floor(index / numOfCols) // 0-7
const x = indexOfX * itemWidth
const y = indexOfY * itemHeight
const color = valueToColor(item)
// biome-ignore lint/suspicious/noArrayIndexKey: SVG elements require a unique key
return <rect key={index} x={x} y={y} width={itemWidth} height={itemHeight} fill={color} />
})}
</svg>
}

/**
* Convert value to color
* @param value 0-7
* @returns color string
*/
const valueToColor = (value: number): string => {
return COLORS[value]
}

const is128Bit = (value: bigint): boolean => {
return value >= BigInt(0) && value <= BigInt('0xffffffffffffffffffffffffffffffff')
}

/**
* Convert 128bit value to int8 array
* @param value 128bit value
* @returns int8 array of 64 elements (0-7)
*/
export const from128bitTo8bitArray = (value: bigint): Uint8Array => {
let value_ = value
const bytes = new Uint8Array(64)
for (let i = 0; i < 64; i++) {
// take 2 bits from the value
const bit = Number(value_ & BigInt(0b11)) // 0-3
// shift 2 bits
value_ >>= BigInt(2)
bytes[i] = bit
}
return bytes
}
73 changes: 73 additions & 0 deletions packages/biticon/src/bitShuffle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const S_BOX: number[] = [ // AES S-Box
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
] as const;


const applySBox = (input: Uint8Array): Uint8Array => {
const output = new Uint8Array(input.length);
for (let i = 0; i < input.length; i++) {
output[i] = S_BOX[input[i]];
}
return output;
}

const xorShift = (data: Uint8Array): Uint8Array => {
const output = new Uint8Array(data.length);
for (let i = 0; i < data.length; i++) {
const prev = data[(i - 1 + data.length) % data.length];
const next = data[(i + 1) % data.length];
output[i] = data[i] ^ prev ^ next;
}
return output;
}

const applyMultipleTimes = (input: Uint8Array, times: number): Uint8Array => {
let output = input;
for (let i = 0; i < times; i++) {
output = xorShift(output);
output = applySBox(output);
output = xorShift(output);
}
return output;
}


export const applySBox128bitBigintTo128bitBigint = (input: bigint): bigint => {
const inputArray = bigint128bitToUint8Array(input)
const outputArray = applyMultipleTimes(inputArray, 16)
return uint8ArrayToBigint128bit(outputArray)
}

const bigint128bitToUint8Array = (value: bigint): Uint8Array => {
let value_ = value
const bytes = new Uint8Array(16)
for (let i = 0; i < 16; i++) {
const bit = Number(value_ & BigInt(0xff)) // 0-255
value_ >>= BigInt(8)
bytes[i] = bit
}
return bytes
}

const uint8ArrayToBigint128bit = (value: Uint8Array): bigint => {
let value_ = BigInt(0)
for (let i = 0; i < 16; i++) {
value_ |= BigInt(value[i]) << BigInt(i * 8)
}
return value_
}
8 changes: 8 additions & 0 deletions packages/biticon/src/colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Based on the colormap viridis from the colormap package (CC0)
// https://github.com/BIDS/colormap/blob/master/colormaps.py
export const COLORS = [
'#440154',
'#30678d',
'#35b778',
'#fde724',
] as const
1 change: 1 addition & 0 deletions packages/biticon/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './base'
4 changes: 4 additions & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"dev": "tsup --config ../../tsup.config.mjs --watch"
},
"devDependencies": {
"@kitsuyui/react-binary": "workspace:^",
"@kitsuyui/react-biticon": "workspace:^",
"@kitsuyui/react-clock": "workspace:^",
"@kitsuyui/react-dekamoji": "workspace:^",
"@kitsuyui/react-editablelabel": "workspace:^",
Expand All @@ -39,6 +41,8 @@
"react-use": "^17.5.0"
},
"peerDependencies": {
"@kitsuyui/react-binary": "workspace:latest",
"@kitsuyui/react-biticon": "workspace:latest",
"@kitsuyui/react-clock": "workspace:latest",
"@kitsuyui/react-dekamoji": "workspace:latest",
"@kitsuyui/react-editablelabel": "workspace:latest",
Expand Down
24 changes: 24 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
"@kitsuyui/react-binary#build": {
"outputs": ["packages/binary/dist/**"]
},
"@kitsuyui/react-biticon#build": {
"outputs": ["packages/biticon/dist/**"]
},
"@kitsuyui/react-components#build": {
"dependsOn": [
"@kitsuyui/react-binary#build",
Expand All @@ -51,6 +54,7 @@
"@kitsuyui/react-textfield#build",
"@kitsuyui/react-measure#build",
"@kitsuyui/react-treemap#build",
"@kitsuyui/react-biticon#build",
"@kitsuyui/react-wavebox#build"
],
"outputs": ["packages/components/dist/**"]
Expand All @@ -72,6 +76,7 @@
"@kitsuyui/react-measure#build",
"@kitsuyui/react-treemap#build",
"@kitsuyui/react-wavebox#build",
"@kitsuyui/react-biticon#build",
"@kitsuyui/react-editablelabel#build",
"@kitsuyui/react-style-bulma#build"
],
Expand Down

0 comments on commit 2c55242

Please sign in to comment.