Stack이란?

: 데이터를 일시적으로 저장하는 방법

LIFO (Last In First Out, 후입선출)의 구조로나중에 들어온 데이터가 먼저 나가는 방식

 

배열을 크게 잡고 사용하여도 되지만 학습을 위하여 malloc을 사용하였다. 

 

typedef struct 
{
    int size;
    int idx;
    int *data;
}Stack;

void DelStack(Stack *s){
    free(s->data);
}


int initStack(Stack *s, int size){
    s->size=size;
    s->idx=-1;
    s->data=(int*)malloc(sizeof(int)*(s->size));
    if(s->data!=NULL){
        return 1; 
    }else{
        s->size=0;
        return 0; 
    }   
}

구조체 Stack의 멤버 변수로는 스택의 크기 : size , 데이터 index : idx , 데이터 : *data 

initStack을 통해 스택 구조체를 초기화한다. 

idx는 -1로 초기화하고 malloc이 실패했을때 initStack이 0을 리턴하도록 한다. 

int isFullStack(Stack *s){
    if(s->idx>=s->size-1){
        return 1;
    }else{
        return 0;
    }
}
int isEmptyStack(Stack *s){
    if(s->idx==-1){
        return 1;
    }else{
        return 0;
    }
}

int pushStack(Stack *s, int data){
    if(isFullStack(s)){
        // printf("Stack is Full\r\n");
        return 0;
    }else{
        s->idx++;
        s->data[s->idx]=data;
        return 1;
    }
    return 0;
}
int popStack(Stack *s){
    int val=0;
    if(isEmptyStack(s)){
        // printf("stack is Empty");
        return val;
    }else{
        val=s->data[s->idx];
        s->idx--;
        return val;
    }    
}
 

isFullStack : 스택이 가득 찼는지 확인

 

isEmptyStack: 스택이 비었는지 확인 

 

pushStack : 스택에 데이터 삽입

-> 스택이 가득 차지 않았을 경우, 데이터 인덱스를 옮기고 데이터를 넣는다.

idx를 -1로 초기화 하였기 때문에 idx를 먼저 더해서 옮기고 데이터를 넣었다. 만약 idx를 0으로 초기화하였다면 데이터를 넣고 idx를 더해주면 된다. 

 

popStack : 스택에서 데이터 출력

->스택이 비어있지 않을 경우, 데이터를 출력하고 인덱스를 옮긴다. 

 

int SearchStack(Stack *s, int data){
    int i;
    for(i=0;i<s->idx;i++){
        if(data==s->data[i]){
            return i;
        }
    }
    return -1;
}

조금의 응용으로  SearchStack은 스택에 찾으려는 데이터가 있으면 해당 인덱스를 없으면 -1을 리턴하도록 만들어보았다. 

 

만든 함수를 아래와 같이 테스트하였다.

int main(){
    int i;
    Stack stack;
    initStack(&stack,10);
    pushStack(&stack,1);
    pushStack(&stack,2);
    pushStack(&stack,3);
    pushStack(&stack,4);
    pushStack(&stack,5);
    pushStack(&stack,6);
    pushStack(&stack,7);
    pushStack(&stack,8);
    pushStack(&stack,9);
    pushStack(&stack,10);

    pushStack(&stack,11);
    printf("search %d %d\r\n",SearchStack(&stack,7),SearchStack(&stack,-1));    

    for(i=0;i<13;i++){
        printf("% d idx:%d\r\n",popStack(&stack),stack.idx);

    }
    DelStack(&stack);
    return 0;

}

테스트 결과

1. 크기가 10인 사이즈를 만들고 11개의 데이터를 넣었다. - > Stack is Full 출력

2. 데이터가 7인 인덱스와 -1인 인덱스를 서치하였다 -> serach 6 -1 출력

3. 13개의 데이터를 출력하였다 -> 10 idx : 9~~ stack is Empty 0 idx : -1 출력 

 

반응형

디버깅 혹은 출력을 보기 위해 stm에서 printf가 필요할 때가 엄~~~청 많은 것 같다.

 

Uart가 있다면 HAL_UART_Transmit을 통해 아주 쉽게 printf와 비슷한 효과를 낼 수 있다. 

 

어떻게 ?

바로 그냥 문자열을 HAL_UART_Transmit을 이용하여 전송하면 된다. 

void print_str(char *str) {
	HAL_UART_Transmit(&huart1, str, strlen(str), 500);
}

int main(){
...

print_str("START\r\n")
...
}

 

만약 문자열이 아니라 변수의 값을 출력해보고 싶다면?

itoa 함수를 이용하면 출력할 수 있다. 

void print_val(char *strtemp, int val) {
	static char temp[128];
	char *ptr;
	ptr = temp;
	memset(temp, 0x00, 128);
	strcpy(temp, strtemp);
	ptr += strlen(temp);
	itoa(val, ptr, 10);
	HAL_UART_Transmit(&huart1, temp, strlen(temp), 500);
}

int main(){
...
a=10;
print_val("state : ",a);
...
}

간단한 출력할 때 유용하게 사용하는 중.. 

반응형

Hex로 된 데이터를 HexString으로, HexString을 Hex로 변환하는 경우가 종종 있었다. 

그럴때 마다 Hex convert to Hexstring ~~ 등등 구글링을 했었는데....

이번에 만든 걸로 쓸 곳에 쓰고 정리를 해보았다.

 

Hex 를 HexString으로 변환은 말그대로 Hex형태로 된 데이터를 HexString으로 변환하는 것이다.

쉽게 말하면 아래와 같다. 

 

Hex로 된 데이터가 있다. 

uint8_t hex[16]={0xA1,0xA2,0xA3,0xA4,0xB5,0xB6,0xB7,0xB8,0xC9,0xC0,0xCA,0xCB,0xDC,0xDD,0xDE,0xDF};

 

현재는 한 바이트당 hex로 표시된 것을 말 그대로 스트링형태로 바꾸는 것이다. 

예를 들어 0xA1의 경우 1바이트 Hex로 표시되어 있다.

이 때, A와 1을 문자형태로 만들어 0xA1이란 1바이트 Hex를 A1이란 2바이트의 문자열(0x41,0x31)로 만드는 것이다. 

 

반대로 HexString 은 A1이란 스트링을 보이는 그대로 0xA1이란 Hex로 만드는 것이다. 

 

설명이 애매한가....? 난 이해가 되니깐.. ㅎ 

 

먼저 Hex를 HexString으로 바꾸는 건 아래와 같다. 

int hex_convert_hexstring(uint8_t* data, uint8_t len, uint8_t* result){
	int i=0;
	int idx=0;
	for(i=0;i<len;i++){
		result[idx++]=(*(data+i))>>4 & 0x0f;
		result[idx++]=(*(data+i))& 0x0f;
	}

	for(i=0;i<idx;i++){
		if(result[i]>=10){
			result[i]=result[i]-10+'A';
		}else{
			result[i]=result[i]+'0';
		}
	}
    return idx;
}

data에는 변환할 Hex 데이터, len에는 data의 길이, result는 결과를 저장할 배열을 넣는다. 

변환된 후, result의 데이터 길이가 retrun된다. 

간단히 설명하자면..... 

Hex 값을 4비트씩 나누어서  result에 넣고, 문자열로 표현하기에 result에 넣은 값을 문자열 형태로 변환해주면 된다.

이 때, 10이 hex로 표현되면 A로 표현되므로 문자열로 변환하기 위해서 10이상의 값은 10을 빼주고 문자 'A'만큼 더해준다. 

이렇게 되면 10일 경우는 문자 'A', 11인경우 11-10+'A' 가 되므로 'A'+1 인 'B'가 된다. 

10보다 작을 때는 문자 '0'을 더해주어 0~9를 문자 형태로 변환한다. 

아스키 코드 표를 보면서 하면 더 쉽게 이해가 될 것이다. 

 

 

다음으로는 HexString에서 Hex로 변환하는 법이다. 

int hexstring_convert_hex(uint8_t* data,uint8_t len,uint8_t* result){
	int idx=0;
	int i=0;
	for (i=0;i<len;i++){
		if(*(data+i)>='A'){
			*(data+i)=*(data+i)-'A'+10;
		}else{
			*(data+i)=*(data+i)-'0';
		}
	}
	i=0;
	for(idx=0;idx<len/2;idx++){
		result[idx]=*(data+i++)<<4  | *(data+i++) & 0x0f;
	}
    return idx;
}

data에 hexstring을 넣고, len에 hexstirng의 길이를 넣고, result에 hex결과를 저장할 배열을 넣는다. 

변환된 후, hex데이터의 길이가 return 된다.

아까와는 반대로 hexString의 문자를 hex로 만들고, 2바이트씩 합쳐준다. 

 

실행 예시는 다음과 같다. 

int main(){
    int i=0;
    int string_len=0;
    int hex_len=0;
    uint8_t hex[16]={0xA1,0xA2,0xA3,0xA4,0xB5,0xB6,0xB7,0xB8,0xC9,0xC0,0xCA,0xCB,0xDC,0xDD,0xDE,0xDF};
    uint8_t hex_string[64];
    uint8_t hex_result[64];

    string_len=hex_convert_hexstring(hex,sizeof(hex),hex_string);

    for(i=0;i<string_len;i++){
        printf("%C ",hex_string[i]);
    }
    printf("\r\n");
    printf("%s\r\n",hex_string); //A1A2A3A4B5B6B7B8C9C0CACBDCDDDEDF

    hex_len=hexstring_convert_hex(hex_string,string_len,hex_result);
    
    for(i=0;i<hex_len;i++){
        printf("%02X ",hex_result[i]); //A1 A2 A3 A4 B5 B6 B7 B8 C9 C0 CA CB DC DD DE DF
    }
    printf("\r\n");
    return 0;

}

주석처리가 실제 프린트된 내용인데 잘 변환된 것 같다.

 

끝 !

반응형

'지식저장소 > C' 카테고리의 다른 글

[C] Ring Buffer / 링버퍼 구현  (0) 2022.03.29
[C/BOJ] 9012 괄호 - Stack이용  (0) 2022.02.16
[C]구조체를 이용한 Stack 구현  (0) 2022.02.16
[C/STM32] Uart로 printf 대신하기  (0) 2022.01.14
[C] ARIA 128 암호화  (2) 2021.09.03

Linux에서 Golang 으로 GPIO를 제어해보았다. 

 

GPIO란? GPIO(General Purpose Input Output)는 일반적인 용도의 입출력 포트를 의미한다. 

 

NewGPIO로 구조체를 만들고, Pin(string)으로 제어할 GPIO 핀번호를 정한다. 

Out , In 으로 dir을 정하고 , High, Low로 값을 정하고, PinRead 로 해당 GPIO 값이 0인지 1인지 읽을 수 있다. 

PinUnexport로 사용을 종료할 수도 있다. 

 

핀을 초기화하고, 방향과 Low,High를 정해 사용할 수 있다. 

예를 들면

g:=NewGPIO()

g.Pin("1")

g.Out().High()

g.In().Low() 등등 

 

전체코드는 다음과 같다.

// gpio.go
package gpio

import (
	"io/ioutil"
	"log"
	"os"
)

const (
	gpioBasePath     = "/sys/class/gpio"
	gpioExportPath   = "/sys/class/gpio/export"
	gpioUnexportPath = "/sys/class/gpio/unexport"
)

type GPIO struct {
	pin string
}

func NewGPIO() GPIO {
	return GPIO{}
}

func (g GPIO) Pin(pin string) GPIO {
	g.pin = pin
	if _, err := os.Stat(gpioBasePath + "/gpio" + g.pin); os.IsNotExist(err) {
		err := ioutil.WriteFile(gpioExportPath, []byte(g.pin), 0666)
		if err != nil {
			log.Println(err)
		}
	}
	return g
}

func (g GPIO) Out() GPIO {
	err := ioutil.WriteFile(gpioBasePath+"/gpio"+g.pin+"/direction", []byte("out"), 0666)
	if err != nil {
		log.Println(err)
	}
	return g
}

func (g GPIO) In() GPIO {
	err := ioutil.WriteFile(gpioBasePath+"/gpio"+g.pin+"/direction", []byte("in"), 0666)
	if err != nil {
		log.Println(err)
	}
	return g
}

func (g GPIO) High() bool {
	err := ioutil.WriteFile(gpioBasePath+"/gpio"+g.pin+"/value", []byte("1"), 0666)
	if err != nil {
		log.Println(err)
		return false
	}
	return true
}

func (g GPIO) Low() bool {
	err := ioutil.WriteFile(gpioBasePath+"/gpio"+g.pin+"/value", []byte("0"), 0666)
	if err != nil {
		log.Println(err)
		return false
	}
	return true
}

func (g GPIO) PinRead(pin string) byte {
	value, err := ioutil.ReadFile(gpioBasePath + "/gpio" + pin + "/value")
	if err != nil {
		log.Println(err)
	}

	return value[0] - 48
}

func (g GPIO) PinUnexport(pin string) bool {
	err := ioutil.WriteFile(gpioUnexportPath, []byte(pin), 0666)
	if err != nil {
		log.Println(err)
		return false
	}
	return true
}

끝 !

반응형

리눅스를 쓰기 시작하면서 윈도우와는 달리 키보드를 더 많이 쓰게 되었다....

정말 기초 중의 기초이면서 가장 먼저하고 거의 매일하는 일은 터미널 창 띄우기가 아닐까..?

터미널 창을 이용하여 이것저것하면서 내가 실제 터미널 관련해서 쓰는 단축키들은 아래와 같다. 

 

먼저 터미널 창을 띄워야 한다. 

터미널 창 띄우기 : CTRL + ALT + T

 

터미널 창을 띄우는 것으로 터미널 관련 사용법의 반을 익혔다.

 

작업을 하다보면 터미널 창이 많아지는 경우가 있는데 터미널 창을 많이 열다 보면 찾기도 힘들고 보기도 힘들다

그래서 1개의 터미널 창에 인터넷 브라우저와 같이 Tab을 만든다. 

터미널 탭 만들기 : CTRL + SHIFT + T

 

탭 창을 만들었으니 터미널 창 자체를 여러개 만들지 않고 1로 깔끔하게 쓸 수 있게 되었다.

여러개의 탭을 만들었으면 앞에서 부터 1,2,3 번호가 있다고 생각하고 아래의 단축키를 누르면 해당 탭으로 이동할 수있다. 

터미널 탭 바꾸기 : ALT + 해당 탭 창 번호

 

작업이 끝나서 탭을 닫아야할 경우 해당 탭으로 이동해서 터미널 탭 종료를 한다.

터미널 탭 종료 : CTRL + SHIFT + W

 

작업하는 터미널 창 크기가 작아서 내용이 짤리는가? 문제없다.

터미널 창 최대화 : ALT + F10

 

창을 다시 줄이려면 ALT + F10을 한번 더 눌러주자.

 

터미널 창 자체를 닫는 건 국룰인 ALT + F4 또는 CTRL + SHIFT + Q

 

터미널 창 안의 내용을 복사하려면 복사할 부분을 드래그 등으로 선택한 후, 

터미널 창 내에서 복사 : CTRL + SHIFT + C

 

터미널 창 내에 붙여넣기를 하고 싶다면?

터미널 창 내에 붙여넣기 : CTRL +SHIFT + V

 

로그 파일을 따로 만들지 못해 터미널 출력에서 특정 단어를 찾아야한다면 ? 

터미널 창 내에서 찾기 : CTRL + SHIFT + F

 

터미널 창을 열고 닫고, 탭을 만들고 이동하고 닫고,  창을 키우고 줄일 수 있고,

복사하고, 붙여넣고, 특정 문자를 찾을 수 있는 정도면 불편함없이 사용하는 것 같다. 

 

기초이지만 자주쓰는 터미널 창 명령어

끝 !

 

반응형

'지식저장소 > 이것저것' 카테고리의 다른 글

[Linux ] minicom 로그 저장/캡쳐하기  (0) 2022.06.22

이번엔 Golang에서 제공되는 라이브러리를 이용하여 AES CFB 암호화를 하고 Base64로 인코딩 하였다.

암호화 이론은.... 설명할 수준까지는 안되기에 다른 자료나 블로그들을 참조하였다

CFB로 암호화하고 Binary 데이터를 String 형태로 변경하는 Base64까지 적용하였다.

실행할 때 Key값을 인자로 넣고 실행을 한다.

그 후, 암호화할 내용들을 입력하면 끝 !

Base64를 적용하지 않으려면 EncryptAESCFB까지만 진행하면 된다.

윈도우용으로 사용하려면 크로스 컴파일 하면 무난히 사용 가능할 듯 하다.

- 소스코드

// main.go
package main

import (
	"bufio"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"encoding/base64"
	"fmt"
	"os"
)

var iv = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}

func main() {
	keyValue := os.Args[1]
	key := make([]byte, 16)
	md5Key := md5.Sum([]byte(keyValue))

	for i, v := range md5Key {
		key[i] = v
	}

	scanner := bufio.NewScanner(os.Stdin)

	for scanner.Scan() {
		ucl := scanner.Text()
		fmt.Println("----------------------------------")
		fmt.Println("Plain Data : ")
		fmt.Printf("[")
		for i, val := range ucl {
			if i == len(ucl)-1 {
				fmt.Printf("0x%02X", val)
			} else {

				fmt.Printf("0x%02X, ", val)
			}
		}
		fmt.Printf("]\n")
		fmt.Println(ucl)

		encrypted := make([]byte, len(ucl))
		err := EncryptAESCFB(encrypted, []byte(ucl), key, iv)
		if err != nil {
			panic(err)
		}
		fmt.Println("\nEncrypted Data : ")
		fmt.Printf("[")
		for i, val := range encrypted {
			if i == len(encrypted)-1 {
				fmt.Printf("0x%02X", val)
			} else {

				fmt.Printf("0x%02X, ", val)
			}
		}
		fmt.Printf("]\n")

		// fmt.Printf("[ 0x% X, ]\n", encrypted)
		fmt.Println(string(encrypted))

		sEnc := base64.StdEncoding.EncodeToString(encrypted)
		fmt.Println("\nbase64 Data :")

		fmt.Printf("[")
		for i, val := range sEnc {
			if i == len(sEnc)-1 {
				fmt.Printf("0x%02X", val)
			} else {

				fmt.Printf("0x%02X, ", val)
			}
		}
		fmt.Printf("]\n")
		fmt.Println(sEnc)

	}

}

func EncryptAESCFB(dst, src, key, iv []byte) error {
	aesBlockEncrypter, err := aes.NewCipher([]byte(key))
	if err != nil {
		return err
	}
	aesEncrypter := cipher.NewCFBEncrypter(aesBlockEncrypter, iv)
	aesEncrypter.XORKeyStream(dst, src)
	return nil
}

func DecryptAESCFB(dst, src, key, iv []byte) error {
	aesBlockDecrypter, err := aes.NewCipher([]byte(key))
	if err != nil {
		return nil
	}
	aesDecrypter := cipher.NewCFBDecrypter(aesBlockDecrypter, iv)
	aesDecrypter.XORKeyStream(dst, src)
	return nil
}

실행 화면

테스트는 실행할 때, ABCD라는 키값을 넣고 1234ABC 라는 평문을 넣었다.

CFB 암호화와 추가로 Base64 인코딩을 했을때 까지 단계별로 확인할 수 있다.

출력은 Hex와 String으로 모두 확인 가능하다.

혹여나 다른 프로그램 코드에서 IP 나 Port 등과 같은 정보들을 암호화 해서 하드코딩 했는데

다음에 변경하여야 할 경우 혹은 테스트용으로 잠시 변경해야할 경우...

나름 유용하게 쓸 수 있지 않을까...? 는.... 내가 몇번 잘 썻다...ㅎ

끝!

 

반응형

KISA에서 제공하는 ARIA 암호화 예제를 이용한 간단한 암호화를 해보았다. 

 

16바이트로 딱 떨어지지 않는 데이터라도 제로 패딩을 하고 암호화를 하도록 기존의 함수를 조합한 정도뿐이지만...

개인적으로 함수 하나만 넣으면 결과를 얻을 수 있어 사용하기 편하게 만들었다.

 

기존 EncKeySetup 함수와 DeckeySetup 함수를 그대로 이용하였고 단순히 128비트 암호화를 사용하기 쉽게 함수만 2개 추가하였다 

 

사용한 master_key는 단순히 16 바이트로 단순히 1~16까지 넣었다. 

추가한 함수는 EnCrypt와 DeCrypt 이다. 

EnCrypt는 다음과 같다. 

암호화할 데이터와 결과를 얻을 버퍼, 평문의 길이를 넣으면 암호화된 결과의 길이가 리턴된다. 

 

DeCrypt는 다음과 같다. 

복호화 할 데이터와 결과를 얻을 버퍼, 복호화할 데이터의 길이를 넣으면 복호화 된 길이가 리턴된다.

 

해당 두 함수를 이용하여 ARIA128_test 함수를 만들었다

실행결과는 다음과 같다.

암호화를 하고 복호화를 해도 원래 데이터가 잘 나오는 것을 확인하였지만... 

검색을 통해 알아낸 ARIA 암. 복호화 모듈로 검증을 해보았다. 

 

EnCrypt
Decrypt

암.복호화 모두 일치하는 것을 확인을 했다.

 

더 많은 테스트가 필요하겠지만.. 일단은 성공...?

반응형

[]byte를 INT보다  HEX 데이터로 출력할 때가 생각보다 많은 것 같다. 

 

 

 위와 같은 iv []byte를 fmt.Println하면 아래와 같이 int형으로 출력이 된다. 

이를 hex형태로 보고 싶다면 예전에는 for문을 이용해서 아래와 같이 출력했다.....(미련하게...)

 

 

하지만 형식지정자를 사용하면 쉽게 출력 할 수 있었다.

좀 더 보기 좋게 하기 위해서 %뒤에 한칸 띄워서 각 바이트들이 띄워쓰기가 되도록 하고 \n을 넣어 마지막에 줄바꿈도 추가하였더니 

 

깔끔하게 출력되었다. 

 

엄청 기본적이고 쉬운 내용이였지만 그냥 지나쳐버려 비효율적인 방법을 반복하고 있었다......

반응형

+ Recent posts