All posts by admin

Ressu kokeilu: satunnaisuuden leviämisen nopeuttaminen

Ressu kokeilu yrittää lisätä satunnaisuuden muodostamista siirtämällä kellojonon luvun jälkeen alimmasta eli “tarkimmasta” bitistä satunnaisuutta seuraavien merkkien ylempiin bitteihin. Se ei tietenkään pysty lisäämään satunnaisuutta kelloon mutta se nopeuttaa satunnaisuuden leviämistä kaikkiin puskurin bitteihin.

/*
 * Ressu−satunnaislukugeneraattori versio 1.0 (ressugen.c)
 *
 * (c)2013−2018 Jari Kuivaniemi, Kaikki oikeudet pidätetään!
 */
#include <stdio.h>
#include <sys/time.h>

unsigned char *ressu_name = "Ressu 1.0";

#ifndef OWN_CLOCK

unsigned char clockbyte() /* JariK 2013 */
{
  struct timeval tv;

  gettimeofday(&tv,NULL);

  return(tv.tv_usec & 0xff);
}

#endif

void ressu_genbytes(int size, unsigned char *buffer, int b) /* JariK 2013 */
{
  int c,d,e,f;

  f=0;
  for(c=0;c<8*b;c++) {
    for(d=0;d<size;d++) {
      e=buffer[d];
      e=((e&0x80)>>7) | ((e&0x7f)<<1);
      buffer[d]=e^clockbyte();
    }
    for(d=0;d<size;d++) {
      e=buffer[d]&1;
      buffer[(d+1)%size]^=(e<<1);
      buffer[(d+2)%size]^=(e<<2);
      buffer[(d+3)%size]^=(e<<3);
      buffer[(d+4)%size]^=(e<<4);
      buffer[(d+5)%size]^=(e<<5);
      buffer[(d+6)%size]^=(e<<6);
      buffer[(d+7)%size]^=(e<<7);
    }
    for(d=0;d<size;d++) {
      f=(f+buffer[d])%size;
      e=buffer[d];
      buffer[d]=buffer[f];
      buffer[f]=e;
    }
  }
}

Uusi kappale koodissa on tuo toinen “for(d=0;”  -kappale. For d-toistorakenne käy koko puskurin läpi ja xor:aa sarjan alimman bitin seuraavan merkin toiseksi alimpaan merkkiin, kolmannen merkin kolmenneksi alimpaan bittiin, neljännen merkin neljänneksi alimpaan bittiin jne. Tätä toistetaan kunnes ensinnäkin tämän hetkisen sarjan kahdeksan merkkiä on käsitelty ja toisaalta kunnes kaikki puskurin  alimmat bitit on käsitelty.

Seuraa kierrosrakenne, joka vaihtaa korttipakkamaisesti kaikki puskurin merkit toiseen satunnaiseen merkkiin (f). Tämän jälkeen edelliset bitit eivät enää ole vierekkäin vaan ne siirtyvät satunnaisiin kohtiin puskuria.

Jos haluat katsella bittien vaihteluha, kopioi seuraava tulostusluuppi uuden kierrosrakenteen ensimmäiseksi ja viimeiseksi.

fprintf(stdout,"\n%2d %3d",c,d);
for(g=0;g<8;g++) {
  fprintf(stdout," %02x",buffer[d+g]);
}

JK: Toinen tapa nopeuttaa satunnaisuuden siirtymistä koko puskuriin on lisätä jokaiseen merkkiin satunnaisuutta seuraavista merkeistä: Ensimmäisessä luupissa sarjan ensimmäistä merkkiä muutetaan toisen, kolmannen ja neljännen merkkien perusteella. Tässä satunnaisuus saadaan kahden merkin and:in perusteella. Ensimmäinen pari on toinen ja kolmas  merkki, toisen parin muodostavat toinen ja neljäs merkki, kolmannen parin muodostavat kolmas ja neljäs merkki.  Parin puoliskot and:ataan keskenään ja näillä kolmella merkillä xor:ataan ensimmäistä merkkiä. Tämän jälkeen voidaan ajaa taas “korttipakan sekoitus”, jolloin bitit jotka vaikuttavat toisiinsa eivät ole enää vierekkäin. Toisaalta tässähän käydään koko puskuri läpi merkki kerrallaan, joten seuraavat merkit ilman korttipaikan sekoitustakin “häviävät”.

for(d=0;d<size;d++) { // see sha2
  buffer[d%size]^=
    ((buffer[(d+1)%size]&(buffer[(d+2)%size])) ^
     (buffer[(d+1)%size]&(buffer[(d+3)%size])) ^
     (buffer[(d+2)%size]&(buffer[(d+3)%size])) );
}

Toinen versio on samanlainen mutta tässä käytetään or:ia

for(d=0;d<size;d++) {
  buffer[d%size]^=
    ((buffer[(d+1)%size]|(buffer[(d+2)%size])) ^
     (buffer[(d+1)%size]|(buffer[(d+3)%size])) ^
     (buffer[(d+2)%size]|(buffer[(d+3)%size])) );
}

Kolmannessa käytetään satunnaisbittien hakemiseen ynnäystä ja satunnaisbittien yhteenlaskussa vieläkin xor:ia.

for(d=0;d<size;d++) {
  buffer[d%size]^=
    (unsigned char)(
    (buffer[(d+1)%size]+(buffer[(d+2)%size])) ^
      (buffer[(d+1)%size]+(buffer[(d+3)%size])) ^
    (buffer[(d+2)%size]+(buffer[(d+3)%size])) );
}

Olen näköjään juuttunut tähän ressuun, toivottavasti saan aikaiseksi jotain muuta sanottavaa tertusta seuraavaan postiin.

Ressu versio 1

Tässä oikeastaan ensimmäinen “virallinen” julkaisu Ressu-satunnaislukugenetaattorin versiosta 1.0.

Edit: muokattu julkaisun jälkeisten viikkojen aikana.

Satunnaislukugeneraattorihan koostuu kahdesta funktiosta, clockbyte(), joka palauttaa kellojonon seuraavan merkin, ja ressu_genbytes(), joka on varsinainen generaattori.

Ensimmäinen kierros genbytes() rutiinista vaihtaa jokaisen merkin bittien järjestystä ja summaa merkkiin seuraavan kellojonon merkin. Ajatuksena on että jokainen tuloksena tulevan puskurin bitti koskee kaikkia kellojonon merkin bittejä.

Toinen kierros sekoittaa tulokseksi saadun kellojonon kuin korttipakan käyden koko jonon läpi, ja vaihtaen kunkin merkin satunnaisen merkin kanssa. Satunnainen merkki valitaan kellojonon merkkien perusteella.

Näitä kahta kierrosta suoritetaan b kertaa, jossa b on kierrosten lukumäärä ja määritellään genbytes() kutsussa.

Eli siis kellon tarkin bitti koskettaa merkin tarkinta bittiä, sen jälkeen merkkiä siirretään satunnaisesti toiseen kohtaan puskurissa, tämän jälkeen toinen tarkin bitti kellojonosta koskettaa merkin seuraavaa tarkinta bittiä ja merkki siirretään taas. Eli merkin bitit tulevat täysin satunnaisista kellon kohdista ja kaikkien kellon bittien vaikutuskohdat leviävät tasaisesti puskurissa. Jos kierroksia on kahdeksan, tämä tehdään kahdeksan kertaa, ja puskurin kaikki bitit koskettavat kahdeksan kertaa satunnaista kellosignaalin tarkinta bittiä ja vaihtavat kosketusten välillä paikkojaan.

OWN_CLOCK #define muuttujalla voidaan määritellä oma clock rutiini. Sitä voisi teoriassa käyttää näppäimistölle tai valokuville.

Vielä kerran: ethän käytä tätä generaattoria ainoana generaattorina kriittisiin tarkoituksiin, vaan summaat aina useampia generaattoreita..

/*      
 * Ressu−satunnaislukugeneraattori versio 1.0 (ressugen.c)
 *
 * (c)2013−2018 Jari Kuivaniemi, Kaikki oikeudet pidätetään!
 */
#include <stdio.h>
#include <sys/time.h>

unsigned char *ressu_name = "Ressu 1.0";

#ifndef OWN_CLOCK

unsigned char clockbyte() /* JariK 2013 */
{
  struct timeval tv;

  gettimeofday(&tv,NULL);

  return(tv.tv_usec & 0xff);
}

#endif

void ressu_genbytes(int size, unsigned char *buffer, int b) /* JariK 2013 */
{
  int c,d,e,f;

  f=0;
  for(c=0;c<8*b;c++) {
    for(d=0;d<size;d++) {
      e=buffer[d];
      e=((e&0x80)>>7) | ((e&0x7f)<<1);
      buffer[d]=e^clockbyte();
    }
    for(d=0;d<size;d++) {
      f=(f+buffer[d])%size;
      e=buffer[d];
      buffer[d]=buffer[f];
      buffer[f]=e;
    }
  }
}

Clockbyte() palauttaa yksinkertaisesti alimmat bitit järjestelmän kellosta.

Ressu_genbytes() saa parametreinaan puskurin koon, osoitteen siihen ja kierrosten lukumäärän. Se generoi puskuriin satunnaisbittijonon. Rutiini sisältää kolme kierrosrakennetta, joista c luuppi laskee kierroksia, d laskee puskurin merkkejä (alusta loppuun), e:tä käytetään välimuuttujana, ja f laskee vaihdettavan merkin paikkaa. F:ään lisätään aina puskurin sisältö, eli näin myös kellojono, se vastaa siitä että kun yksikin bitti kellojonossa muuttuu, tulos muuttuu. Tästä tulee periaatteessa mahdollinen ongelma, eli jos kellojono on samanlainen, generaattori generoi samoja bittijonoja uudestaan. Näyttää kuitenkin siltä että kellojonossa on samanlaisia vain noin kilojen kokoisia jaksoja. Tämän perusteella voisi ehkä laskea puskurin pituutta. Jatkossa puskurin pituus on määritelty 65535 merkiksi.

Tässä vielä muutama apurutiini:

Ressu_genbyte() palauttaa yhden ressu generaattorin arpoman merkin. Ressu_genbyte_limit(int limit) palauttaa yhden haluttua rajaa pienemmän merkin (raja on parametrissa limit). ressu_genshort() palauttaa 16 bittisen intin ja ressu_genshort_limit(int limit) palauttaa rajaa pienemmän 16 bittisen luvun. ressu_clear() tyhjentää apurutiinitn ressu_bytes puskurin.

Jättämällä määrittelemättä #define muuttuja RESSU_CLEAR:in voimme estää puskurin tyhjentämisen uuden puskurin luonnin yhteydessä. Jos puskuria ei ole tyhjennetty, voimme alkaa riidellä siitä, kuinka paljon edellisistä biteistä voidaan arvata uuden puskurin bittejä. (todellisuudessahan yhteyttä edelliseen puskuriin ei ole, puolet biteistä vaihtuvat vähintään rounds kertaa).. Toisaalta jos puskuria ei tyhjennetä, satunnaisuus “kertyy” useampien kutsukertojen välillä.

B muuttuja seuraavan koodin alussa kertoo kuinka monta kierrosta generaattoria ajetaan. Se on nyt 45 ja olen ajatellut sen olevan riittävän suuri ettei ongelmia kriittistenkään satunnaisbittien generoinnissa tule. Samoin tuo puskurin pituus on arvaus, jolla ei pitäisi tulla ongelmia. Jos esimerkiksi haluat satunnaislukuja shakki peliin tai sudokujen laatimiseen voit ilman muuta valita pienemmät luvut. (Esimerkiksi kilon puskuri ja pari kierrosta riittävät hyvin perus pelin generaattoriin.) Olen itse tutkinut generaattoria ja tuohon tietoon voi tulla muutoksia. Toisaalta jos noudatat neuvoani ja et käytä tätä generaattoria pelkästään, ei tietenkään haittaa siirtyä pienempiin lukuihin.

#define RESSUBUFLEN 2048
unsigned char ressu_bytes[RESSUBUFLEN];

int ressu_byte=999999;
int ressu_b = 45;
#define aRESSU_CLEAR 2
unsigned char *file_b = "libressu1.b";

int ressu_genbyte()
{
  if(ressu_byte>=ressu_bytes) {
    if(ressu_buffer==NULL)
      ressu_buffersize(ressu_bytes);
#ifdef RESSU_CLEAR
    ressu_clear();
#endif
    ressu_genbytes(ressu_bytes, ressu_buffer,
        ressu_b);
    ressu_byte=0;
  }
  return(ressu_buffer[ressu_byte++]);
}

void ressu_genbytes_xor(unsigned char *buffer,int size)
{
  int c,d;

  for(c=0;c<size;c++)
    buffer[c]^=ressu_genbyte();;
}

int ressu_genbyte_limit(int limit)
{
  int c;

  while((c=ressu_genbyte())>(256/limit)*limit);
  return(c%limit);
}

int ressu_genshort()
{
  return(ressu_genbyte()+256*ressu_genbyte());
}

int ressu_genshort_limit(int limit)
{
  int c;

  while((c=ressu_genshort())>
          (65536/limit)*limit);
  return(c%limit);
}

void ressu_clear()
{
  memset(ressu_bytes,0,sizeof(ressu_bytes));
  ressu_byte=999999;
}

Yksi ongelma on määritellä, miten pitkä kellojonoo tarvitaan, että siinä on riittävästi satunnaisbittejä puskuriin. Yksi tapa tarkastella sitä on päättää että yhdessä kellon vaihdoksessa on yksi bitti, eli jos yhden vaihdoksen pituus on 20 merkkiä ja tarvittavien satunnaismerkkien määrä olisi 2048, kaava olisi 2048*8*20. Tulokseksi saataisiin että b:n arvo olisi sama kuin kellojakson pituus eli 20.

Tässä ohjelma, joka hakee kellojonon pituuden ja palauttaa sen (lisäten 2).

int ressu_calculate_b()
{
  int c,d,ed,count,first,statmax,statlength,b,b2;
  unsigned char stats[512];

  while(clockbyte()!=0);
  while(clockbyte()==0);
  while(clockbyte()!=0);

  for(c=0;c<sizeof(stats);c++)
    stats[c]=0;
  first=1;
  for(c=0;c<65535;c++) {
    d=clockbyte();
    if(first==1) {
      ed=d;
      count=1;
      first=0;
    } else if(ed!=d) {
      ed=d;
      stats[count]++;
      count=1;
    } else {
      count++;
    }
  }

  for(c=0;c<512;c++)
    if(stats[c]>0)
      fprintf(stdout," %d:%2d",c,stats[c]);

  fprintf(stdout,"\n");

  statmax=0;
  statlength=-1;
  for(c=0;c<sizeof(stats);c++) {
    if(stats[c]>=statmax) {
      statmax=stats[c];
      statlength=c;
    }
  }
  fprintf(stdout,"most frequend clockchain size: %d",statlength);
  b2=int_read(file_b);
  fprintf(stdout,", b from file: %d",b2);
  if(b2>=statlength) {
    b=b2;
  } else {
    b=statlength;
    int_write(b,file_b);
    fprintf(stdout,", b written to file: %d",b);
  }
  fprintf(stdout,", returned b: %d\n",b+2);
  fflush(stdout);
  
  return(b+2);
}

int int_read(char *filename)
{
  int i;
  unsigned char buffer[10];
  FILE *fp1;

  i=0;
  
  if((fp1=fopen(filename,"r"))!=NULL) {
    fgets(buffer,sizeof(buffer),fp1);
    i=atoi(buffer);
    fclose(fp1);
  }
  return(i);
}

void int_write(int i, unsigned char *filename)
{
  FILE *fp1;

  if((fp1=fopen(filename,"w"))!=NULL) {
    fprintf(fp1,"%d\n",i);
    fclose(fp1);
  }
}

Jos haluat itse pongata pitkiä duplikaattimerkkijonoja voit aloittaa sen seuraavalla ohjelmalla:

#include <stdio.h>
#include <string.h>
#include <time.h>

#include "ressugen.c"

#define RESSUBYTES 16384*8
//#define RESSUBYTES 16384*48
unsigned char ressu_bytes[RESSUBYTES];

#define VERBOSE 2
#define aFAST 2

time_t now;
#define HTMLTIMEFORMAT "%a, %d %b %Y %H:%M:%S GMT"
unsigned char datestr[128];
char procname[] = "Ressutest v0.01";

void main(int argc,char *argv[])
{
  int c,d,e,limit,dup,first0;
  int low,high,middle;
  time_t now2,now3;

  
  now = time(NULL);
  strftime(datestr, sizeof(datestr),
       HTMLTIMEFORMAT, gmtime(&now));

  fprintf(stdout,"\n");
  fflush(stdout);
  
  fprintf(stdout,"%s\n%s\n\n",procname,datestr);
  fflush(stdout);

  for(;;) {
    for(c=0;c<sizeof(ressu_bytes);c++) {
      ressu_bytes[c]=clockbyte();
    }
    
    now2 = time(NULL);
    
    limit=sizeof(ressu_bytes);;
    low=1;
    high=10000;

    fprintf(stdout,"limit: %d",limit);
    fflush(stdout);

    while(low<=high) {
      middle=(low+high)/2;
#ifdef VERBOSE
      fprintf(stdout,"\nlimit: %d",limit);
      fprintf(stdout,", low:%d",low);
      fprintf(stdout,", high: %d",high);
      fprintf(stdout,", middle: %d",middle);
      fflush(stdout);
#endif
      dup=0;
      for(d=0; d<limit-middle; d++) {
    for(e=d+1; e<limit-middle; e++) {
      if(!memcmp(&ressu_bytes[d],
             &ressu_bytes[e],middle)) {
        dup++;
        e+=middle;
#ifdef FAST
        break;
#endif
      }
    }
#ifdef FAST
      if(dup)
    break;
#endif
      }
#ifdef VERBOSE
      fprintf(stdout,", dup: %d",dup);
      fflush(stdout);
#endif
      if(dup) {
    low=middle+1;
      } else {
    high=middle-1;
      }
    }
#ifdef VERBOSE
    fflush(stdout);
    fprintf(stdout,", middle: %d",middle-1);
#endif
    first0=middle-1;
    dup=0;
    for(d=0;d<limit-(middle-1);d++) {
      for(e=d+1;e<limit-(middle-1);e++) {
    if(!memcmp(&ressu_bytes[d],
           &ressu_bytes[e],middle-1)) {
      dup++;
      e+=middle;
#ifdef FAST
      break;
#endif      
    }
      }
#ifdef FAST
      if(dup)
    break;
#endif
    }
    if(dup!=0)
      first0++;
#ifdef VERBOSE
    fprintf(stdout,", dup: %d",dup);  
    fflush(stdout);
#endif
#ifdef VERBOSE
    fprintf(stdout,", middle: %d",middle);
    fflush(stdout);
#endif
    dup=0;
    for(d=0;d<limit-middle;d++) {
      for(e=d+1;e<limit-middle;e++) {
    if(!memcmp(&ressu_bytes[d],
           &ressu_bytes[e],middle)) {
      dup++;
      e+=middle;
#ifdef FAST
      break;
#endif
    }
      }
#ifdef FAST
      if(dup)
    break;
#endif
    }
    if(dup!=0)
      first0++;
#ifdef VERBOSE
    fprintf(stdout,", dup2: %d",dup);
    fflush(stdout);
#endif
#ifdef VERBOSE
    fprintf(stdout,", middle: %d",middle+1);
    fflush(stdout);
#endif
    dup=0;
    for(d=0;d<limit-(middle+1);d++) {
      for(e=d+1;e<limit-(middle+1);e++) {
    if(!memcmp(&ressu_bytes[d],
           &ressu_bytes[e],middle+1)) {
      dup++;
      e+=middle;
#ifdef FAST
      break;
#endif
    }
      }
#ifdef FAST
      if(dup)
    break;
#endif
    }
    if(dup!=0)
      first0++;
    now3 = time(NULL);
#ifdef VERBOSE
    fprintf(stdout,", dup3: %d",dup);
    fflush(stdout);
#endif
    fprintf(stdout,", first0: %d",first0);

    int time;

    time=now3-now2;

    fprintf(stdout,", took ");
    if((time/60)>0) {
      fprintf(stdout,"%d minutes, ",time/60);
      time%=60;
    }
    fprintf(stdout,"%d seconds.\n",time);
    fflush(stdout);
  }
}

Seuraavassa mallitulokset edellisestä ohjelmasta: ensimmäisessä listassa VERBOSE ei ole määritelty. Toisessa ja kolmannessa VERBOSE on määritelty. Toisessa listassa FAST on määritelty, kolmannessa FAST ei ole määritelty. Limit on puskurin koko. Low, high ja middle ovat binäärihaun muuttujat. Dup kertoo onko duplikaatteja. Dup on 0 tai 1, jos FAST on määritelty, muuten tuplamerkkijonojen lukumäärä. First0  on ensimmäinen pituus, jolla ei ole tuplamerkkijonoja.

limit: 131072, first0: 1818, took 17 minutes, 10 seconds.
limit: 131072, first0: 1618, took 13 minutes, 23 seconds.
limit: 131072, first0: 2790, took 15 minutes, 16 seconds.
limit: 131072, first0: 2616, took 11 minutes, 21 seconds.
limit: 131072, first0: 2598, took 16 minutes, 35 seconds.

limit: 131072, low:1, high: 10000, middle: 5000, dup: 0
limit: 131072, low:1, high: 4999, middle: 2500, dup: 0
limit: 131072, low:1, high: 2499, middle: 1250, dup: 1
limit: 131072, low:1251, high: 2499, middle: 1875, dup: 1
limit: 131072, low:1876, high: 2499, middle: 2187, dup: 0
limit: 131072, low:1876, high: 2186, middle: 2031, dup: 1
limit: 131072, low:2032, high: 2186, middle: 2109, dup: 1
limit: 131072, low:2110, high: 2186, middle: 2148, dup: 0
limit: 131072, low:2110, high: 2147, middle: 2128, dup: 1
limit: 131072, low:2129, high: 2147, middle: 2138, dup: 1
limit: 131072, low:2139, high: 2147, middle: 2143, dup: 1
limit: 131072, low:2144, high: 2147, middle: 2145, dup: 0
limit: 131072, low:2144, high: 2144, middle: 2144, dup: 1, middle: 2143, dup: 1, middle: 2144, dup2: 1, middle: 2145, dup3: 0, first0: 2145, took 13 minutes, 54 seconds.
limit: 131072
limit: 131072, low:1, high: 10000, middle: 5000, dup: 0
limit: 131072, low:1, high: 4999, middle: 2500, dup: 0
limit: 131072, low:1, high: 2499, middle: 1250, dup: 1
limit: 131072, low:1251, high: 2499, middle: 1875, dup: 0
limit: 131072, low:1251, high: 1874, middle: 1562, dup: 1
limit: 131072, low:1563, high: 1874, middle: 1718, dup: 0
limit: 131072, low:1563, high: 1717, middle: 1640, dup: 1
limit: 131072, low:1641, high: 1717, middle: 1679, dup: 0
limit: 131072, low:1641, high: 1678, middle: 1659, dup: 1
limit: 131072, low:1660, high: 1678, middle: 1669, dup: 0
limit: 131072, low:1660, high: 1668, middle: 1664, dup: 0
limit: 131072, low:1660, high: 1663, middle: 1661, dup: 1
limit: 131072, low:1662, high: 1663, middle: 1662, dup: 1
limit: 131072, low:1663, high: 1663, middle: 1663, dup: 1, middle: 1662, dup: 1, middle: 1663, dup2: 1, middle: 1664, dup3: 0, first0: 1664, took 16 minutes, 0 seconds.

limit: 131072, low:1, high: 10000, middle: 5000, dup: 0
limit: 131072, low:1, high: 4999, middle: 2500, dup: 0
limit: 131072, low:1, high: 2499, middle: 1250, dup: 666
limit: 131072, low:1251, high: 2499, middle: 1875, dup: 0
limit: 131072, low:1251, high: 1874, middle: 1562, dup: 0
limit: 131072, low:1251, high: 1561, middle: 1406, dup: 252
limit: 131072, low:1407, high: 1561, middle: 1484, dup: 96
limit: 131072, low:1485, high: 1561, middle: 1523, dup: 34
limit: 131072, low:1524, high: 1561, middle: 1542, dup: 15
limit: 131072, low:1543, high: 1561, middle: 1552, dup: 5
limit: 131072, low:1553, high: 1561, middle: 1557, dup: 0
limit: 131072, low:1553, high: 1556, middle: 1554, dup: 3
limit: 131072, low:1555, high: 1556, middle: 1555, dup: 2
limit: 131072, low:1556, high: 1556, middle: 1556, dup: 1, middle: 1555, dup: 2, middle: 1556, dup2: 1, middle: 1557, dup3: 0, first0: 1557, took 25 minutes, 46 seconds.
limit: 131072
limit: 131072, low:1, high: 10000, middle: 5000, dup: 0
limit: 131072, low:1, high: 4999, middle: 2500, dup: 469
limit: 131072, low:2501, high: 4999, middle: 3750, dup: 0
limit: 131072, low:2501, high: 3749, middle: 3125, dup: 0
limit: 131072, low:2501, high: 3124, middle: 2812, dup: 102
limit: 131072, low:2813, high: 3124, middle: 2968, dup: 0
limit: 131072, low:2813, high: 2967, middle: 2890, dup: 24
limit: 131072, low:2891, high: 2967, middle: 2929, dup: 0
limit: 131072, low:2891, high: 2928, middle: 2909, dup: 5
limit: 131072, low:2910, high: 2928, middle: 2919, dup: 0
limit: 131072, low:2910, high: 2918, middle: 2914, dup: 0
limit: 131072, low:2910, high: 2913, middle: 2911, dup: 3
limit: 131072, low:2912, high: 2913, middle: 2912, dup: 2
limit: 131072, low:2913, high: 2913, middle: 2913, dup: 1, middle: 2912, dup: 2, middle: 2913, dup2: 1, middle: 2914, dup3: 0, first0: 2914, took 29 minutes, 26 seconds.

 

 

 

Salasanan salakirjoitus tertussa

Kaikki oikeudet tietenkin pidätetään. Viimeinen versio ohjelmasta löytyy seuraavasta linkistä: moijari.com:5002. Ressu satunnaislukuja löytyy osoitteesta: moijari.com:5001. Ikuinen kalenteri osoitteesta: moijari.com:5003.

Salasana on salakirjoitettuna muodossa:

sha256:ee84f5affcf47d:51567b8c4c20db2a00ea24eaae125a26f5c7c87fdbe5b90a02225fc97008b49a (oheinen salasana on muuten "SalaSana1234") ja vielä
unsigned char *peppper="l7IKrgcLMgsl_4Wv";

Alussa salatun salasanan tallennusformaatti, kaksoispisteen jälkeen alkaa salt:ti ja seuraavan kaksoispisteen jälkeen alkaa kryptattu salasana.

Tämä ensimmäinen rutiini salakirjoittaa password kentässä olevan salasanan ja muodostaa encpasswd kentän joka sisältää salasanan salakirjoitetussa muodossa.

int encryptpasswd(unsigned char encpasswd[ENCPASSWD_SIZE],unsigned char *password)
{
  int c,d;

  HashCtx ctx;

  unsigned char salt[7];
  unsigned char salthex[15];
  unsigned char twodigit[3];
  unsigned char temp[HashLen];
  unsigned char temphex[HashLen*2+1];

  memset(encpasswd,0,ENCPASSWD_SIZE);

  ressu_genbytes2(sizeof(salt),salt);

  ...

Ohjelman alussa varataan erilaisia ohjelman tarvitsemia muistialueita. Salt kentän tarkoituksena on erilaistaa sama salasana eri käyttäjillä ja vaikeuttaa kryptattujen salasanojen hakemiston muodostamista. Salt on salakirjoittamattomana salakirjoitetussa salasanassa , ja kopioidaan sellaisenaan salasanan tarkastukseen. Temp kenttää taas käytetään kryptatun salasanan tallettamiseen..

Seuraavat ohjelmarivit muuttavat salt:in heksamuotoon:

  ...
  for(c=0;c<sizeof(salt);c++) {
    sprintf(twodigit,"%02x",salt[c]);
    strcat(salthex,twodigit);
  }
  ...

Seuraava kappale tekee varsinaisen salakirjoituksen, Salakirjoitus tehdään sha256 tiivisterutiinilla. HASHROUNDS on tällä hetkellä sata tuhatta, ja siihen on valittu mahdollisimman suuri luku, joka silti pitää logon-vaiheen keston kohtuullisena. Pepper on aloitusarvo rutiinille.

  ...
  memset(temp,0,sizeof(temp));
  strcpy(temp,pepper);
  for(c=0;c<HASHROUNDS;c++) {
    HashInit(&ctx);
    HashUpdate(&ctx,temp,sizeof(temp));
    HashUpdate(&ctx,salt,sizeof(salt));
    HashUpdate(&ctx,password,strlen(password)+1);
    HashFinal(temp,&ctx);
  }
  ...

Seuraavassa edellisen koodikappaleen tulos temp muuttujassa laitetaan heksamuotoon:

  ...
  temphex[0]='\0';
  for(c=0;c<sizeof(temp);c++) {
    sprintf(twodigit,"%02x",temp[c]);
    strcat(temphex,twodigit);
  }
  ...

Kootaan salakirjoitettu salasana:

  ...
  strcpy(encpasswd,"sha256:");
  strcat(encpasswd,salthex);
  strcat(encpasswd,":");
  strcat(encpasswd,temphex);
  ...

Ja tyhjennetään vielä välikentät, jottei niitä vuoda ulos aliohjelmasta: ja funktion lopussa vielä return lause (0=ok).

  ...
  memset(salt,0,sizeof(salt));
  memset(salthex,0,sizeof(salthex));
  memset(temp,0,sizeof(temp));
  memset(temphex,0,sizeof(temphex));

  return(0);
}

Salasanan tarkistus funktio tekee oikeastaan samat asiat, hieman muuteltuna: koodikappaleen loppu tarkistaa salakirjoitetun salasanan tallennusformaatin ja  hakee salt:in heksamuodossa. Salt haetaan parametrina annetusta salakirjoitetusta salasanasta.

int checkpasswd(unsigned char encpasswd[ENCPASSWD_SIZE],unsigned char *password)
{
  int c,d,e,byte,ok;
  unsigned char *p,*s;
  HashCtx ctx;

  unsigned char salt[7];
  unsigned char salthex[15];
  unsigned char twodigit[3];
  unsigned char temp[HashLen];
  unsigned char temphex[HashLen*2+1];
  unsigned char encpasswd2[128];

  p=encpasswd;

  if(strncmp(p,"sha256:",7))
    return(1);

  p+=7;
  s=salthex;
  while(*p!=':') {
    *s++=*p++;
  }
  *s='\0';
  ...

Salasanan tarkistusrutiini hakee saltin satunnaisbittigeneraattorin sijasta annetusta salakirjoitetusta salasanasta.

Seuraava siirtää heksana olevan saltin binäärimuotoon:

  ...
  c=0;
  s=salthex;
  for(;;) {
    if((d=gethexdigit(*s++))==-1) break;
    if((e=gethexdigit(*s++))==-1) break;
    byte=d*16+e;
    salt[c++]=byte;
  }
  ...

Varsinainen salasanan kryptausosa on samanlainen kun edellisessä crypt rutiinissa:

  ...
  memset(temp,0,sizeof(temp));
  strcpy(temp,pepper);
  for(c=0;c<HASHROUNDS;c++) {
    HashInit(&ctx);
    HashUpdate(&ctx,temp,sizeof(temp));
    HashUpdate(&ctx,salt,sizeof(salt));
    HashUpdate(&ctx,password,strlen(password)+1);
    HashFinal(temp,&ctx);
  }
  ...

Muokanaan salakirjoitettu salasana temp kentästä temphex kenttään heksamuotoon:

  ...
  temphex[0]='\0';
  for(c=0;c<sizeof(temp);c++) {
    sprintf(twodigit,"%02x",temp[c]);
    strcat(temphex,twodigit);
  }
  ...

Muodostetaan varsinainen salasana salakirjoitetussa muodossa:

  ...
  strcpy(encpasswd2,"sha256:");
  strcat(encpasswd2,salthex);
  strcat(encpasswd2,":");
  strcat(encpasswd2,temphex);
  ...

Vertaillaan salakirjoitettuna annettua salasanaa ja selväkielisestä salasanasta muodostettua salakirjoitettua salasanaa keskenään, tyhjennellään kenttiä ja palautetaan totuusarvo:

  ...
  if(!strcmp(encpasswd,encpasswd2)) {
    fprintf(stdout,"\n*************ok");
    ok=0;
  } else {
    fprintf(stdout,"\n*************error");
    ok=1;
  }

  memset(encpasswd2,0,ENCPASSWD_SIZE);
  memset(salt,0,sizeof(salt));
  memset(salthex,0,sizeof(salthex));
  memset(temp,0,sizeof(temp));
  memset(temphex,0,sizeof(temphex));

  return(ok);
}

Tässä vielä gethexdigit:

int gethexdigit(char h)
{
  int byte;

  if(h>='0' && h<='9')
    byte=h-'0';
  else if(h>='a' && h<='f')
    byte=h-'a'+10;
  else if(h>='A' && h<='F')
    byte=h-'A'+10;
  else
    byte=-1;

  return(byte);
}

 

Hash:in käyttö tertun kyselyn käsittelyssä

Kaikki oikeudet tietenkin pidätetään. Viimeinen versio ohjelmasta löytyy seuraavasta linkistä: moijari.com:5002

Tällä hetkellä ajattelen että terttu jakautuu kahteen osaan, sovellusosaan ja tietokantaosaan. Sovellusosa sisältää kaikki sovelluskohtaiset asiat, kuten tertussa olevat logon-rutiini, sovellus-rutiini, prosessien jatkaminen, talletus-rutiini ja nuo sarakkeiden siirtelyt prosessien yhteydessä.

Tietokantaosa sisältää toiminnot fetch ja save, jotka ovat yksinkertaisia toimintoja. Fetchillä luetaan rivejä ja save:lla talletetaan niitä.

Fetch lyhyesti

(polveilevaa jaarittelua) Fetch koostuu vaiheista, Ensimmäinen vaihe muodostaa asiakkaan antamasta kyselystä vakiomuotoiset versiot. Tässä yksi versio kyselyn normalisointirutiinista, voit itse kokeilla tehdä muut versiot voi olla myös että minä lisään koodia postin loppuun..

void skk_querystring1(char *query, unsigned char *newquery)
{
  int value;
  unsigned char *p,*q;
  HashCtx ctx;

  p=query;
  q=newquery;

  skk_skipwhite(&p);
  while(*p!='\0') {
    if(*p=='\'') {
      *q++=*p++;
      while(*p!='\'' && *p!='\0')
        *q++=*p++;
      *q++='\'';
      if(*p=='\'')
        p++;
    } else if(*p=='=') {
      p++;
      *q++=' ';
      *q++='=';
      *q++=' ';
    } else if(*p=='\"') {
      p++;
      value=0;
      while(*p!='\"' &&*p!='\0') {
        p++;
        value=1;
      }
      if(*p=='\"')
        p++;
        if(value==1)
          *q++='?';
    } else if(*p==',') {
      p++;
      *q++=',';
      *q++=' ';
    } else if(*p==' ' || *p=='\t') {        
      skk_skipwhite(&p);
      *q++=' ';
    } else
      p++;
  }
  *q='\0';
}

Huomaathan että tämä normalisointi normalisoi myös ‘määrä’=’isotilaus’. Ja että toisaalta se ei toimi kyselylle ‘määrä'<‘saldo’. Tämä on vielä hiukan hakusessa.

Seuraavasta kyselystä (‘asiakasnumero’ = “1000” ,  ‘asiakkaan nimi’) tulostetaan ainakin seuraavat versiot: versioissa vakiotoiminta on siten, että vaihdetaan useamman välilyönti-tabi yhdistelmät yhteen välilyöntiin. Yhtäsuuruusmerkki vaihdetaan välilyönti yhtäsuuruusmerkki-välilyöntiin. Hipsulla ‘\” annetut kentän nimet tallennetaan siten että ne joko alkavat rivin alusta, tai erotetaan toisista sanoista välilyönnein. Lainausmerkillä ‘”‘ annetut kentän arvot siirretään samalla tavalla kuin hipsu kentät. Samoin kyselyn kentät lajitellaan aakkosjärjestykseen. Mallina olevassa kyselyssä olevat kentät ovat tietenkin jo aakkosjärjestyksessä.

(‘asiakasnumero’, ‘asiakkaan nimi’)

tällä versiolla voidaan tarkistaa, onko tietueet talletettu siten, että yhdessä hash koodilla hajasijoitetussa tietoryhmässä on kaikki tietueet. Esimerkkinå voisi olla:

(‘kuukauden numero’=”1″, ‘kuukauden nimi’=”Tammikuu”)
(‘kuukauden numero’=”2″, ‘kuukauden nimi’=”Helmikuu”)

Tietueet jatkuvat tietysti samalla tavalla loppuun asti. Kun tiedosto avataan, sieltä valitaan kyselyyn mätsäävät tiedot.

Toinen vakioitu versio kyselystä on seuraavana:

(‘asiakasnumero’ = ?, ‘asiakkaan nimi’)

Tällä hash koodilla löydetään tieto siitä, onko tämä tieto talletettu tuossa muodossa, eli siis kannattaako (‘asiakasnumero’= “1000” “asiakkaan nimi”) tyylistä kyselyä hakea. Ilmeisesti kaikilla tietojen perusavaimilla voi tai pitää olla tämä versio. Huomaa pienet tiedostot ja ensimmäinen kyselyversio.

Seuraava versio kyselystä on malli, jolla hajasijoitetusta “tietokannasta” haetaan tieto:

(‘asiakasnumero’ = “1000” ,  ‘asiakkaan nimi’)

Ja tässäkin vakiomuotoisesta kyselystä muodostetaan hash koodi, josta tehdään x syvyyksinen hakemistorakenne ja jos tiedosto löytyy, sieltä pitäisi löytyä kyselyn tulos. Eli kyselyn vastaukseen tarvitaan n 4 tiedoston avausta, hash koodin laskentaa ja kyselymerkkijonon normalisointia.

Toisessa vaiheessa tarkistetaan löytyykö kyselyn perusteella avain muistista, muistissa olevat kyselyt talletetaan muodossa:

struct query {
    unsigned char *query;
    struct set *sets;
    struct query *next;
}:

Haetaan vain muistilista läpi “tarkalla” kyselyversiolla, jos kysely löytyy listasta, sillä on tässä rakenteessa sets kentässä ratkaisu. Jatkossa lisätään vielä viimeiset tapahtumat eli muutokset lokista. (katso perusavain määrittely jatkossa)

Muistissa olevat kyselyt pitää järjestää siten että usein käytetyt kyselyt ovat ensimmäisenä listassa, silloin voidaan poistaa harvoin käytetyt kyselyt siten, että poistetaan viimeiset kysely vastaus parit.

Kolmannessa vaiheessa haetaan pelkät kenttien nimet sisältävällä versiolla olisiko kysymys pienestä taulusta (tai miksei suuremmastakin, jos kyselyn suoritusnopeudella ei ole väliä). Jos tiedosto löytyi, me saamme siitä vastauksen. Vastaukseen lisätään vielä viimeisten lokitapahtumien muutokset.

Neljännessä vaiheessa haetaan kysäri(?)versiolla onko meillä ratkaisua tälle kyselymuodolle, jos on käytetään kenttien arvot sisältävää versiota, taas saadaan tulos, johon lisätään lokitapahtumat ja palautetaan tiedot asiakkaalle (asiakasohjelmalle).

Viidennessä versiossa kyselyn vastaus haetaan lokista (vrt perusavain mallit).

Kaikissa kyselymalleissa palautettava vastaus lisätään muistissa olevien kyselyiden joukkoon.

Save

Save toiminto lisää talletuskutsussa olevat tietueet lokin loppuun ja kirjoittaa lokin levylle. Ilmeisesti jossain kohtaa kirjoitetaan nämä lokientrit eri talletustavoilla käytettyihin tiedostoihin (muisti, pieni taulu, taulu avaimella jne.)

Hakemistorakenne

Hash koodista muodostetaan hakemistorakenne seuraavasti:

Hash koodilla: abcdefghijklmnopqrstuvwxyz hakemistorakenne voi olla: kanta/ab/cd/ef/gh/ij/kl/mn/op/qr/st/uv/wx/yz tai kanta/ab/cdefghijklmnopqrstuvwxyz tai jotakin siltä väliltä.

Perusavaimista

Edit: Perusavaimen käsittelyyn on mielessäni kaksi mallia, ensimmäinen on yksinkertainen malli, jossa tietueen avaim(ena/ina) ovat aina saman nimiset kentät, muilla tietueen kentillä ei ole merkitystä, eli selvitään yksinkertaisella määrittelyllä:

(‘memberid’=”asiakasnumero”, ‘key’=”1″)(‘memberid’=”toimittajanumero”, ‘key’=”1″)
(‘memberid’=”tuotenumero”, ‘key’=”1″)
(‘memberid’=”tilausnumero”, ‘key’=”1″)
(‘memberid’=”tilausrivin numero”, ‘key’=”1″)
(‘memberid’=”toimitusnumero”, ‘key’=”1″)
(‘memberid’=”toimitusrivin numero”, ‘key’=”1″)
(‘memberid’=”laskunumero”, ‘key’=”1″)
(‘memberid’=”laskurivin numero”, ‘key’=”1″)
(‘memberid’=”tapahtumanumero”, ‘key’=”1″)
(‘memberid’=”tapahtumarivin numero”, ‘key’=”1″)

Edit: Olen yleensä pitäytynyt tiukasti näissä kolmessa neljässä taulussa, mutta annetaan nyt hiukan ajatuksen lentää:

(‘memberid’=”bussinumero”, ‘key’=”1″)
(‘memberid’=”autonumero”, ‘key’=”1″)
(‘memberid’=”toimenpidenumero”, ‘key’=”1″).

Edit: Voit keksiä itse oman alasi tietueiden avaimet. Toisessa mallissa kaikki tietueen kentät määrittelevät tietuetyypin, ja näistä kentistä jotkut ovat avaimia, esimerkiksi:

(‘memberid’=”asiakasnumero”, ‘key’=”1″)
(‘memberid’=”asiakkaan nimi”)
(‘memberid’=”asiakkaan osoite”)
(‘memberid’=”asiakkaan postitoimipaikka”)
(‘memberid’=”asiakkaan postiosoite”)
(‘memberid’=”asiakkaan maa”)

Edit: Tottakai tietueella voi olla ‘setid’ kenttä, joka nimeää nämä kenttä yhdistelmät. Esimerkiksi edellisessä set id voisi olla ‘asiakas’, Tertussa tähän asti on ollut vain sovelluksia, joilla voi olla otsakkeita ja rivejä.

Edit: (out there) Toki näistä voi keksiä vielä monimutkaisempia vaihtoehtoja, joissa esimerkiksi jonkun kentän sisältö määrittää tietueella olevat kentät:

(‘memberid’=”toimenpidenumero”)
(‘memberid’=”toimenpidetyyppi”)
(‘toimenpidetyyppi=”öljyn vaihto”, ‘memberid’=”tuotenumero”)
(‘toimenpidetyyppi=”öljyn vaihto”, ‘memberid’=”huomiot”)(‘toimenpidetyyppi=”renkaan vaihto”, ‘memberid’=”uusi paine”)(‘toimenpidetyyppi=”renkaan vaihto”, ‘memberid’=”huomiot”)

Edit: Nämä tuovat monimutkaisuutta enemmän kuin on tarpeen. Tämän tyyppiset ratkaisut kuitenkin ratkeavat automaattisella navigaatiolla, tässä vain tarvitaan tietuetta tai ainakin tuota toimenpidetyyppiä mukaan hakuun, jossa haetaan tietueen kenttiä.

Levytilan vähennystä

Jos kyselyyn vastataan hakemalla kyselyyn perustuvalla hash koodilla relevantit tietueet, tiedosto voi olla perinteisen terttu (tai skk1) formaatin mukaan:

(‘asiakasnumero’=”1000″, ‘asiakkaan nimi’=”Asiakas 1″)

tai tiivistää sitä hiukan kirjoittamalla kenttien nimet vain otsakkeeseen: (kyselystähän kenttien nimiä ei voi katsoa, koska ne ovat lajiteltuna aakkosjärjestykseen, eli alkuperäisen kyselyn nimillä ei ole merkitystä.

‘asiakasnumero’, ‘asiakkaan nimi’
“1000”, “Asiakas 1”

Tietueiden “splittaus”

Edit: Tämä ajatus liittyy myös tiedon talletustilan vähentämiseen. Jos teemme kyselyn, Esimerkiksi:

’tilausnumero’, ’tilauksen asiakasnumero’, ’tilauksen asiakkaan nimi’, ’tilauksen tuotenumero’, ’tilauksen tuotteen nimi’,

kysely muodostuu otsakkeiden tiedoista ja tilausrivien tiedoista, eli kyselyn tulos on:

’tilausnumero’=”4000″, ’tilauksen asiakasnumero’=”1000″, ’tilauksen asiakkaan nimi’=”Asiakas 1″, ’tilauksen tuotenumero’=”3000″, ’tilauksen tuotteen nimi’=”Tuote 1″
’tilausnumero’=”4000″, ’tilauksen asiakasnumero’=”1000″, ’tilauksen asiakkaan nimi’=”Asiakas 1″, ’tilauksen tuotenumero’=”3000″, ’tilauksen tuotteen nimi’=”Tuote 1″
’tilausnumero’=”4000″, ’tilauksen asiakasnumero’=”1000″, ’tilauksen asiakkaan nimi’=”Asiakas 1″, ’tilauksen tuotenumero’=”3000″, ’tilauksen tuotteen nimi’=”Tuote 1″

Ja siinä on otsakkeen osalta aika paljon toistoa, koska kaikki otsakekentät ovat samat samassa tilauksessa. Tässä tapauksessa voitaisiin jakaa sovellus kaikkiin eri rivimalleihin, eli otsake olisi oma tietuemallinsa, rivi olisi oma tietuemallinsa, ja ne vain yhdisteltäisiin vasta luvun yhteydessä,

 

HTTP ja HTTPS palvelimen pääohjelma

Kirjoitin uuden version terttu palvelimen pääohjelmasta. Nyt se pystyy vastaamaan myös https viesteihin. Kokeiluversiossa ohjelma antaa satunnaislukuja, ja sitä voi ajaa näistä linkeistä http://moijari.com:5001 ja https://moijari.com:5001.

En vielä ostanut certifikaattia moijari.com:ille, joten moijari.com nettiosoitteelle pitää luoda poikkeus, jos haluaa sitä käyttää https:llä.

Ohjelman alussa ovat c tyyliset includelauseet, joilla luodaan erilaisia muuttujia, rakenteita ja funktioiden mallikutsuja:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <openssl/ssl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>

/* See: http://h41379.www4.hpe.com/doc/83final/ba554_90007/ch04s03.html */

#include <netdb.h>

Aiemmissa tärkein on ehkä tuo <openssl/ssl.h>, joka käytännössä kuvaa nämä tämän ohjelman SSL alkuiset rutiinit. See kappaleessa on itseasiassa nettisivu, jota käytin apuna rakennuksessa.

#define DEFAULT_PORT "5001"
#define backlog 5

int s,news;
char *cert_file = "cert.pem";
char *privatekey_file = "key.pem";

char htmlin[32768], html1[32768], html2[32768], *html;

int usehttps=1;
int usehttpthruhttps=0;

Tässä tärkeimmät ehkä porttinumero, tiedostot, joista https avaimet ja sertifikaatti luetaan ja puskurit asiakkaan viestin lukuun ja kirjoitukseen (in=luku, 1 ja 2 kirjoitus).

Pääohjelman alussa määritellään pääohjelman tarvitsemia muuttujia, ja kutsutaan https liittymän “alustamiseen” liittyviä asioita.

void main(int argc,char *argv[])
{
  int c, listenfd, status, bytes, addr_size,
      len, callid;
  unsigned char timebuf[128];

  time_t now;

  SSL_METHOD *method=NULL;
  SSL_CTX *ctx=NULL;
  SSL *ssl;
  X509 *peer_cert;

  struct sockaddr sa_serv;
  struct sockaddr_in sa_cli;
  struct addrinfo hints;
  struct addrinfo *res;

  char buffer[32768],*p;
  callid=0;

  signal(SIGPIPE,SIG_IGN);

  procname=argv[0];
#ifdef OLD1
  if(usehttps) {
    myport=HTTPS_PORT;
  } else {
    myport=DEFAULT_PORT;
  }
#else
  myport=DEFAULT_PORT;

Alun kokonaislukumuuttujat (int) ovat perinteistä liittymää varten. Perinteisessä liittymässä http tiedon siirto tehdään socket, bind, listen, accept, read- ja write kutsujen avulla. https tiedon siirto tehdään SSL alkuisten kutsujen avulla. edelleen struct komennolla määritellyt muistialueet ovat perinteisiä funktioita varten. signal() funktiolla ohitetaan ilmeisesti writen katketessa lähetetty PIPe signaali. Procnameen talletetaan ohjelman nimi komentorivin ensimmäisestä sanasta. Ohjelman nimeä käytetään virheilmoituksissa. Oletusportti on aina DEFAULT_PORT, jonka arvo on tällä hetkellä 5001. Järjestelmäfunktioista saa kuvauksen komennolla $ man [komento], esimerkiksi $ man SSL_read tai $ man read.

Seuraavassa pätkässä ovat ensimmäiset SSL-rutiinit, näitä ajetaan vain yhden kerran ohjelman suorituksen alussa. usehttps flagillä määritellään käytetäänkö ohjelmassa HTTPS:ää. Eli tässä tapauksessa jos https on käytössä kutsutaan SSL-funktiot SSL_library_init(),  OpenSSL_add_ssl_algorithms(), SSL_load_error_strings(), SSLv23_server_method(), SSL_CTX_new(), SSL_CTX_use_certificate_file(), SSL_CTX_use_PrivateKey_file(), SSL_CTX_load_verify_locations.

   if(usehttps) {

    SSL_library_init();

    OpenSSL_add_ssl_algorithms();

    SSL_load_error_strings();

    if((method=(SSL_METHOD *)    
      SSLv23_server_method())==NULL) {
      fprintf(stderr,"\n%s: cannot SSLv3_server_method()", procname);
      fflush(stderr);
    }

    if((ctx=SSL_CTX_new(method))==NULL) {
      fprintf(stderr,"\n%s: cannot SSL_CTX_new()", procname);
      fflush(stderr);
    }

    if(SSL_CTX_use_certificate_file(ctx,
      cert_file, SSL_FILETYPE_PEM)<=0) {
      fprintf(stderr,"\n%s: cannot SSL_CTX_use_certificate()", procname);
      fflush(stderr);
    }

    if(SSL_CTX_use_PrivateKey_file(ctx,
      privatekey_file, SSL_FILETYPE_PEM)<=0) \
    {
      fprintf(stderr,"\n%s: cannot SSL_CTX_use_certificate()", procname);
      fflush(stderr);
    }

    if(!SSL_CTX_load_verify_locations(ctx, 
      cert_file, NULL)) {
      fprintf(stderr,"\n%s: cannot SSL_CTX_verify_locations()", procname);
      fflush(stderr);
    }
  }

Tössö ensimmäiset perinteiset kutsut: näitä kutsutaan kanssa vain kerran: getaddrinfo, socket(),  bind(),  listen().

  memset(&hints, 0, sizeof(hints);
  hints.ai_family = AF_UNSPEC;                   
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;

  if ((status = getaddrinfo(NULL, myport, &hints, &res)) != 0) {
    fprintf(stderr, "\n%s: getaddrinfo error: %s",
            procname,gai_strerror(status));
    fprintf(stderr, ", error code %d\n", status);
    fflush(stderr);
  }

  if((s = socket(res->ai_family, res->ai_socktype, res->ai_protocol))==-1) {
    fprintf(stderr, "%s: socket(), error: %d\n", procname, errno);
    perror("socket");
    fflush(stderr);
  }

  if(bind(s, res->ai_addr, res->ai_addrlen)==-1) {
    fprintf(stderr,"\n%s: cannot bind(), error: %d\n", procname, errno);
    perror("bind");
    fflush(stderr);
  }

  freeaddrinfo(res);

  if((listenfd=listen(s,backlog))==-1) {
    fprintf(stderr,"\n%s: cannot listen()\n", procname);
    perror("listen");
    fflush(stderr);
  }

Seuraava on ns pääluuppi, joka suoritetaan aina kun veppilomakkeelle kirjoitetaan tietoja ja painetaan nappulaa. Täytetyt tiedot ovat read (tai SSL_read kutsulla luetussa htmlin muuttujassa ja lähetettävä tieto kootaan htmlin:in perusteella html1 ja html 2 tauluihin. Lähetettävää tietoa varten on kaksi taulua, koska taulu 1 sisältää rivin, jossa on taulun 2 merkkien lukumäärä.

Jokaisella weppitapahtumalla käydään läpi accept(), SSL_new(), SSL_set_fd, SSL_accept(),

 for(;;) {
    usehttpthruhttps=0;
    addr_size=sizeof(sa_cli);
    news=accept(s, (struct sockaddr *)&sa_cli, &addr_size);

    fprintf(stdout,"\nConnection from %x, port %d",
            sa_cli.sin_addr.s_addr,
            sa_cli.sin_port);
    if(usehttps) {
      if((ssl=SSL_new(ctx))==NULL) {
        fprintf(stderr,"\n%s: cannot SSL_new()", procname);
        fflush(stderr);
      }

      if(SSL_set_fd(ssl,news)!=1) {
        fprintf(stderr,"\n%s: cannot SSL_set_fd()", procname);
        fflush(stderr);
      }

      if((status=SSL_accept(ssl))<0) {
        if(status==-1 && SSL_get_error(ssl,status)==1) {
          fprintf(stdout,"\nUsing http thru https");
          usehttpthruhttps=1;
        } else {
          fprintf(stderr,"\n%s: cannot SSL_accept(), status: %d, SSL error: %d",  procname, status, SSL_get_error(ssl,status));
          fflush(stderr);
        }
      }
    }
    if(usehttps && usehttpthruhttps==0) {

      peer_cert=SSL_get_peer_certificate(ssl);
      if(peer_cert==NULL) {
        fprintf(stdout,", No peer certificate");
        fflush(stdout);
      }
    }

Tässä on ajateltu, että jos SSL_accept kaatuu virheeseen 1, yhteys ei ole HTTPS yhteys vaan http yhteys. HTTP yhteyden merkiksi laitetaan muuttujaan usehttpthruhttps 1.

Sitten onkin readin vuoro.

   memset(htmlin,0,sizeof(htmlin));

   if(usehttps && usehttpthruhttps==0) {
     if((status=SSL_read(ssl, htmlin,
       sizeof(htmlin)))<1) {
       fprintf(stderr,"\n%s: cannot SSL_read(), status: %d, SSL_error: %d", pr\
ocname, status, SSL_get_error(ssl,status));
       fflush(stderr);
     }
     htmlin[bytes]='\0';
     fprintf(stdout,"\nreceived %d chars, \"%s\"\n", status, htmlin);
   } else {
    if((bytes=read(news, htmlin, sizeof(htmlin)
      ) )==-1) {
      fprintf(stderr,"\n%s: cannot read()\n",procname);
      perror("bind");
      fflush(stderr);
    }
    htmlin[bytes]='\0';
    fprintf(stdout,"\nreceived %d chars,\"%s\"\n", bytes, htmlin);
   }

Eli käytetään SSL_read:id tai read:ia sen mukaan, onko kyseessä https vai http yhteys. htmlin kenttään tulee asiakkaan kenttämuutokset ja nappula ja write:en tai SSL write:en muodostetaan html tai bootstrap tai vastaava lauseita.

Ja vielä lopuksi sivunmuodostus (ja write lauseet):

    html=html1;
    html[0]='\0';

    html_printf("HTTP/1.0 200 OK\r\n");
    html_printf("Location: \r\n");
    html_printf("Server: %s\r\n", programname);
    now = time(NULL);
    strftime(timebuf, sizeof(timebuf), HTMLTIMEFORMAT, gmtime(&now));

    html_printf("Date: %s\r\n", timebuf);

    html=html2;
    html[0]='\0';

    html_printf("\n<!DOCTYPE html>\r\n");
    html_printf("<html lang=\"fi\">");

    html_printf("<head>");
    html_printf("</head>");

    html_printf("<body>");

    html_printf("<h1>Hello, world!</h1>");

    html_printf("</body>");

    html_printf("</html>");

    len=strlen(html2);
    html=html1;
    html_printf("Content-Length: %d",len
    html_printf("\r\n\r\n");

Eli puskuriin 1 laitetaan protokolla, palvelin, kellonaika ja päivämäärä. Kakkospuskuriin laitetaan html lauseita sisältävä sivu (html_printf() on muuten aiemmassa postissa). Ykköspuskuriin laitetaan kakkospuskurin pituuden sisältävä rivi ja seuraavassa write lauseet ykkös ja kakkospuskurille.

   if(usehttps && usehttpthruhttps==0) {
      if((status=SSL_write(ssl, html1, strlen(html1)))<1) {
        fprintf(stderr,"\n%s: cannot SSL_write(), status: %d, SSL error: %d",
                procname, status, SSL_get_error(ssl,status));
        fflush(stderr);
      }
      if((status=SSL_write(ssl, html2, strlen(html2)))<1) {
        fprintf(stderr,"\n%s: cannot SSL_write(), status: %d, SSL error: %d",
                procname, status, SSL_get_error(ssl,status));
        fflush(stderr);
      }
    } else {
      if((bytes=write(news, html1, strlen(html1)))==-1) {
        fprintf(stderr,"\n%s: cannot write()\n", procname);
        perror("write");
        fflush(stderr);
      }
      if((bytes=write(news, html2, strlen(html2)))==-1) {
        fprintf(stderr,"\n%s: cannot write()\n", procname);
        perror("write");
        fflush(stderr);
      }
    }

Kirjoitetaan ykkös ja kakkospuskurit joko SSL_write:llä tai writellä().

Ja lopetetaan tämä input output askel vapauttamalla tuo ssl ctx alue ja suljetaan kierroksen alussa acceptilta saatu news tiedosto.

    if(usehttps && usehttpthruhttps==0) {
      fprintf(stdout,"\nSSL connection using %s", SSL_get_cipher(ssl));
      fflush(stderr);
      SSL_free(ssl);
    }
    if(close(news)==-1) {
      fprintf(stderr,"\n%s: cannot close()\n", procname);
      perror("close");
      fflush(stderr);
    }
    fprintf(stdout,"\ncallid: %d\n", callid++);
    fflush(stdout);
  } /* takaisin for(;;) luupin alkuun */

Jos meillä on tästä for(;;) luupista lopetusmahdollisuus, lopetuksessa vielä vapautetaan SSL_CTX_free komennolla ctx muuttuja.

SSL_CTX_free(ctx);

Prosessit jatkoa 2

Kaikki oikeudet tietenkin pidätetään. Viimeinen versio ohjelmasta löytyy seuraavasta linkistä: moijari.com:5002

Edellisestä terttu postista on kulunut jo sen verran paljon aikaa (tuo aiempi satunnaislukugeneraattori+=opinnäyte) että nyt tuli tarve jatkaa. Tässä postissa ei aiemmasta poiketen ole mukana koodia, tämä on enemmänkin pohdiskelua tai jaarittelua (25000 sivuhakua muuten). Olen aiemmin kirjoittanut vierasavainten käsittelyn syöttölomaketta varten (http://moijari.com/?p=260)(http://moijari.com/?p=209). Seuraavaksi ohjelmassa on save nappulan tarvitsema prosessien hallinta. Nämä ovat oikeastaan saman toiminnon toistoa. Tällä hetkellä ajatuksena on, että “vierasavainkäsittelyn” lisäksi tarvitaan ainakin:

-ehtolause (esimerkiksi tilaus-toimitus tietovirrassa toimituspäivä<=kuluvapäivä (toimitetaan vasta toimituspäivänä), ja toimitus-laskutus tietovirrassa toimitettu==”1″ (laskutetaan vasta toimituksen jälkeen), varastosaldon laskennassa vapaasaldo>tilattumäärä) (sallitaan tilaus vain kun varastossa tilattu määrä tuotteita)
-perusavainten täyttäminen (kun luodaan tilaus, tai  automaattisesti uusi toimitus tai lasku).
-laskutoimitus (tilauksen summa=rivin summa, varastosaldo=varastosaldo+määrä, jne)
-summaus (tilauksen laskutettu määrä, kun tilauksessa on useampia laskuja)
-sijoitus (toimitettu=”1″, laskutettu=”1″ jne.)

Seuraavassa tilaus-sovelluksen määrittelyt:

('tilausnumero', 'tilauksen asiakasnumero', 'tilauksen asiakkaan nimi', 'tilauspäivä', 'tilauksen summa')
('tilausnumero', 'tilausrivin numero', 'tilauksen tuotenumero', 
 'tilauksen tuotteen nimi', 'tilauksen tuotteen hinta', 'tilattu määrä', 'rivin summa'

Ja seuraavassa tilauksen vierasavaimen päässä olevat tiedot asiakas ja tuote-sovellukset:

('asiakasnumero', 'asiakkaan nimi', 'asiakkaan osoite', 'asiakkaan postinumero',
 'asiakkaan postitmp')
('tuotenumero', 'tuotteen nimi', 'tuotteen hinta')

Ja seuraavassa noiden väliset suhteet:

('sovellus'="tilaus", 'fromsovellus'="asiakas", 'tosovellus'="tilaus")
('sovellus'="tilaus", 'fromsovellus'="tuote", 'tosovellus'="tilaus")

Eli kun täytetään tilaus tietueita, samanlaiset sarakkeet haetaan asiakas sovelluksesta. Tilauksen otsakkeessa on kentät tilauksen asiakasnumero ja tilauksen asiakkaan nimi, joita vastaavat kentät ovat asiakasnumero ja asiakkaan nimi. Tilauksen riveillä taas on tilauksen tuotteen numero, tilauksen tuotteen nimi ja tilauksen tuotteen hinta. Jos tilaustietueille on määritelty yksilöivä tieto jommasta kummasta, loput kentät täytetään automaattisesti. Mikä tahansa tilauksella oleva asiakkaan (tai tuotteen) tieto voidaan täyttää, jos se on yksilöivä, muut kentät täytetään automaattisesti. Jos esimerkiksi täytetään asiakkaalle numero 1000, nimi haetaan automaattisesti, jos tuo asiakas numero 1000 on olemassa(lähde tietovirta). Jos tilauksen tuotteen hintaan täytetään 10€, tuotteen muut tiedot haetaan tuotteelta, jonka hinta on ainoana tuotteena 10€.

Seuraavassa laajennetaan tätä koko tilaus-toimitus-laskutus tietovirtaan:

('tilausnumero', 'tilauksen asiakasnumero', 'tilauksen asiakkaan nimi', 'tilauspäivä',
 'tilauksen summa')
('tilausnumero', 'tilausrivin numero', 'tilauksen tuotenumero',
 'tilauksen tuotteen nimi', 'tilauksen tuotteen hinta', 'tilattu määrä',
 'tilauksen toimitettu määrä', 'tilauksen laskutettu määrä', 'tilausrivin summa')
('toimitusnumero', 'toimituksen asiakasnumero', 'toimituksen asiakkaan nimi',
 'toimituksen tilauspäivä', 'toimituspäivä', 'toimituksen summa')
('toimitusnumero', 'toimitusrivin numero', 'toimituksen tuotenumero',
 'toimituksen tuotteen nimi', 'toimituksen tuotteen hinta', 'toimituksen tilattu määrä', 'toimitettu määrä', 'rivin summa')
('laskunumero', 'laskun asiakasnumero', 'laskun asiakkaan nimi', 'laskun päivä',
 'tilauspäivä', 'toimituspäivä', 'eräpäivä', 'laskun summa')
('laskunumero', 'laskurivin numero', 'tuotenumero', 'tuotteen nimi', 'tuotteen hinta', 
 'laskurivin tilattu määrä', 'laskurivin toimitettu määrä', 'laskutettu määrä', 'rivin summa')
('asiakasnumero', 'asiakkaan nimi', 'asiakkaan osoite', 'asiakkaan postinumero',
 'asiakkaan postitmp')
('tuotenumero', 'tuotteen nimi', 'tuotteen hinta', 'tuotteen varastosaldo')

Huomaathan että tässä ei ole esimerkiksi osoitteita, ne lisätään tarvittaessa. Ajatus on löytää tekniset toiminnot, joilla ne saadaan toimimaan. Seuraavassa tietovirtamääritykset koko prosessista:

('sovellus'="tilaus", 'fromsovellus'="asiakas", 'tosovellus'="tilaus")
('sovellus'="tilaus", 'fromsovellus'="tuote", 'tosovellus'="tilaus")
('sovellus'="tilaus", 'fromsovellus'="tilaus", 'tosovellus'="toimitus")
('sovellus'="toimitus", 'fromsovellus'="toimitus", 'tosovellus'="laskutus")

Lisäsin edelliseen tilaussovellukseen kentät ’tilauksen toimitettu määrä’ ja ’tilauksen laskutettu määrä’ havainnollistaakseni prosessin kulkua takaisinpäin. Ensimmäisessä tilausriville saadaan tieto, kuinka paljon tilausriviä on toimitettu, ja se kopioituu yksiselitteisesti toimituksen toimitettu määrä kentästä. Jos tilausriviä on toimitettu useammassa toimituksessa, sen pitäisi olla tietysti summa näistä riveistä.

Laskussa on taas ‘laskun tilattu määrä’ kenttä, joka tulee yksiselitteisesti tilauksesta. Kenttä toimii esimerkkinä, jossa kenttä ei tule prosessin edellisestä vaiheesta vaan hyppää taaksepäin. Ilmeinen vaihtoehto olisi lisätä:

('sovellus'="laskutus", 'fromsovellus'="tilaus", 'tosovellus'="laskutus")

mutta tässä on muitakin kenttiä (asiakkaan tiedot, tuotetiedot), jotka kopioituisivat. Tässä taas laskulla voi jossakin tapauksessa olla useampia tilauksia, jolloin laskun tilattu määrä kenttään tulisi niiden summa. Hmmmm.

Samoin lisäsin tuotetietoihin ‘tuotteen vapaasaldo’-kentän. Se havainnollistaa ulkopuolista kenttää, jota ei talleteta käsiteltävään sovellukseen, mutta sillä kuitenkin tehdään käsittelyä tai laskentaa ja se talletetaan sovelluksen ulkopuolelle. Tässä oletetetaan että meillä on vain yksi varasto ja toimitukset lähtevät samana päivänä (vapaasaldo tietysti vaihtelee toimituspäivästä(-ajasta) johtuen), oikeasti ‘vapaasaldo’ olisi esimerkiksi (‘varasto’, ‘varastopaikka’, ‘tuotenumero’, ‘toimituspäivä’, ‘vapaasaldo’) tai vastaavalla tietueella. Edellisistä ‘varasto’ olisi valinnaisena tietona tilauksessa tai asiakkaalle määriteltäisiin lähin varasto, tai se voitaisiin päätellä yksiselitteisesti toimituksessa.

Laskussa on myös ‘laskun toimitettu määrä’ joka tulee yksiselitteisesti toimituksesta/toimituksista.

Jos järjestelmässä olisi asiakaskohtainen hinta, esimerkiksi (‘tuotenumero’, ‘asiakasnumero’, ‘tuotteen hinta’), vierasavainhaku palauttaisi kaksi tietuetta, tuotekohtaisen tuotteen hinnan ja asiakaskohtaisen hinnan (jos molemmat olisivat syötettynä). Yksinkertaisessa versiossa tilauksen tekijä valitsisi useammasta vaihtoehdosta, se toimisi myös muissa valinnoissa (jos esimerkiksi toimittajan valinta tilauksella palauttaisi kaksi toimittajaa). Hmmm.

Tässä on versio tilauksesta, ja toimituksesta jossa varasto on tilauksen riveillä ja toimituksessa toimituksen otsakkeella. Tässä tapauksessa jos tilatut tavarat tulevat varastoista 1 ja 2 tilaus jakaantuu kahdeksi toimitukseksi (esimerkiksi tuotevarastolle ja tarvikevarastolle).

('tilausnumero', 'tilauksen asiakasnumero', 'tilauksen asiakkaan nimi', 'tilauspäivä',
 'tilauksen summa')
('tilausnumero', 'tilausrivin numero', 'tilauksen tuotenumero',
 'tilauksen tuotteen nimi', 'tilauksen tuotteen hinta', 'tilattu määrä',
 'varasto', 'tilauksen toimitettu määrä', 'tilauksen laskutettu määrä', 'tilausrivin summa')
('toimitusnumero', 'toimittava varasto', 'toimituksen asiakasnumero', 'toimituksen asiakkaan nimi',
 'toimituksen tilauspäivä', 'toimituspäivä', 'toimituksen summa')
('toimitusnumero', 'toimitusrivin numero', 'toimituksen tuotenumero',
 'toimituksen tuotteen nimi', 'toimituksen tuotteen hinta', 'toimituksen tilattu määrä',
 'toimitettu määrä', 'rivin summa')

Satunnaislukugeneraattorin uusi käsittely (jatkuu)

Olen jonkin aikaa tutkinut tätä satunnaislukugeneraattoria: ja olen laatinut pari uutta ohjelmaa lähinnä kellosarjan jaksojen tutkimiseksi ja tuon b:n arvon määrittämiseksi. Tertun tekemisessä on ollut pieni luova tauko, mutta palaan siihen kyllä.. Kannattaa ehkä lukaista edellinen posti: http://moijari.com/?p=327

/*
 * (c)2013-2016 Jari Kuivaniemi, All rights reserved!
 */
unsigned char clockbyte()
{
 unsigned char byte;
 unsigned long usec,sec;

 struct timeval tv;

 gettimeofday(&tv,NULL);

 usec=tv.tv_usec;
 sec=tv.tv_sec;

 byte=(usec&0xff);

 return(byte);
}

genbytes(int size, unsigned char *buffer, int b) /* JariK ~2013*/
{
  int c,d,e,f;
  char byte;

  f=0;
  for(c=0;c<8*b;c++) {
    for(d=0;d<size;d++) {
      byte=clockbyte();
      e=buffer[d];
      e=((e&0x80)>>7) | ((e&0x7f)<<1);
      e=e^byte;
      buffer[d]=e;
    }
    for(d=0;d<size;d++) {
      f=(f+buffer[d])%size;
      e=buffer[d];
      buffer[d]=buffer[f];
      buffer[f]=e;
    }
  }
}

Tällä ensimmäisellä ohjelmalla voidaan katsella kello sarjan jonojen pituuksia: (Kellon sarjathan tai variaatiot niissä antavat edelliselle generaattorille satunnaisuutta)

main()
{
  int c,d,oldc,nextc,count,counts[1024];
  unsigned char buffer[BUFSIZE];

  for(c=0;c<1024;c++)
    counts[c]=0;

  for(d=0;d<BUFSIZE;d++)
    buffer[d]=clockbyte();

  oldc=-1;

  for(d=0;d<BUFSIZE;d++) {
    if(oldc==-1) {
      oldc=buffer[d];
      count=1;
    } else if(buffer[d]!=oldc) {
      counts[count]++;
      oldc=buffer[d];
      count=1;
    } else if(buffer[d]==oldc) {
      count++;
    }
  }

  for(c=0;c<1024;c++) {
    if(counts[c]!=0)
      fprintf(stdout," %d(%d)",counts[c],c);
  }
  fprintf(stdout,"\n");
  fflush(stdout);
}

Se tulostaa hitaalla koneella(raspberrypi) tälläisen rivin:

 431521(1) 308527(2)

Hiukan nopeammalla probookilla tälläisen:

 107(1) 101(2) 262(3) 2185(4) 9148(5) 23006(6) 122137(7)

Vielä hiukan nopeammalla inuc:lla tälläisen:

 12(1) 24(2) 22(3) 24(4) 23(5) 27(6) 13(7) 31(8) 22(9) 12(10) 21(11)
 25(12) 23(13) 27(14) 28(15) 26(16) 21(17) 26(18) 24(19) 29(20)
 26(21) 24(22) 22(23) 22(24) 22(25) 24(26) 25(27) 24(28) 12(29)
 21(30) 18(31) 28(32) 29(33) 21(34) 33(35) 38(36) 38(37) 72(38)
 118(39) 184(40) 495(41) 519(42) 1840(43) 736(44) 6856(45) 649(46)
 29676(47) 3708(48)

Ensimmäinen luku on jonojen lukumäärä ja sulkeissa on jonon pituus, Eli Raspberry pi:ssä jonot ovat 1 ja 2 pituisia, ja että reilusti enemmän jonoissa on kahden pituisia. Yleisin jono on siis 2 merkkiä pitkä.

Probookin jonojen pituudet taas vaihtelevat yhden ja seitsemän välillä, ja tässäkin pisintä jonoa on eniten.

Intel Nuc(3.10Ghz):ssa edelleen jonojen pituudet vaihtelevat 1 ja 48 välillä.

Edelliset rivit olivat lähinnä yhteenvetoja kellon sarjoista, seuraavalla ohjelmalla on yritetty päätellä pisimpien sarjojen pituuksia. (jos kello antaa pitkiä samoja merkkejä sisälltäviä sarjoja, se helpottaa huomattavasti kellon arvaamista)

main()
{
  int c,first,lastbits,lastcount,countbits;

  for(c=0;c<SIZE;c++) {
    buffer[c]=clockbyte();
  }

  lastbits=-1;
  lastcount=0;
  first=1;

  for(c=0;c<SIZE;c++) {
    if(lastbits==-1) {
      lastbits=buffer[c];
      countbits=1;
    } else if(lastbits!=buffer[c]) {
      if(!first) {
        fprintf(stdout," ");
      }
      if(countbits<lastcount) {
        fprintf(stdout,"\n");
        first=1;
      }
      fprintf(stdout,"%d",countbits);
      first=0;
      lastbits=buffer[c];
      lastcount=countbits;
      countbits=1;
    } else if(lastbits==buffer[c]) {
      countbits++;
    }
  }
  fprintf(stdout,"\n");
  fflush(stdout);
}

Molemmissa testiin päässeissä laitteissa on yleisin sarja melko pitkä. Tässä listassa tosin ainoastaan jonojen pituudet vaikuttavat mihin summaan jono tulee, eli noissa yleisimmissä sarjoissa merkkien arvot voivat olla erilaisia. Ohjelmaa on ajettu komennolla:

$ ./rn9 | sort | uniq -cd | sort -gr | more
Intel Nuc:

1868891 47 47 47 47 47 47 47 47 48 
 375860 47 47 47 47 47 47 47 48 
 27043 39 
 26097 38 
 20677 47 47 47 47 47 47 47 47 47 48 
 17652 40 
 17511 37 
 15733 47 
 14121 41 42 42 
 13790 47 47 
 12311 41 42 
 12194 47 47 47 
 11962 41 
 11570 47 47 47 47 
 11155 47 47 47 47 47 
 11071 38 40 
 10748 47 47 47 47 47 47 
 10465 38 39 
 10386 39 40 
 10350 42 
 10301 36 
 10081 47 47 47 47 47 47 47 
 9613 45 
 9462 37 39 
 9148 43 
 8713 45 46 46 46 46 46 
 8583 45 47 
 8056 47 47 47 47 47 47 47 47 
 7304 37 40 
 7147 37 38 
 6830 45 46 46 46 46 46 46 
 6437 39 39 
 6229 45 48 
 6002 38 41 

HP Probook:

16694408 7 7 7 7 7 7 8 
4681171 7 7 7 7 7 8 
 356835 5 6 
 200135 7 7 7 7 7 7 7 7 7 8 
 144738 7 7 7 7 7 7 7 8 
  83786 5 5 6 
  25575 7 7 7 7 7 7 7 7 7 7 8 
  20908 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 
  16492 7 
  16318 6 7 8 
  14668 6 8 
  13082 3 
   9285 6 7 7 8 
   7329 7 7 
   7052 7 7 7 
   6833 7 7 7 7 
   6666 7 7 7 7 7 
   6336 4 
   5163 7 7 7 7 7 7 

Tämänhetkinen päätelmäni on että jos b:n arvo on niin korkea, että genbytes() palauttaa aina uniikkeja arvoja(parhaimmillaan 2^(bits-1) uniikkia arvoa, kellosta saadaan riittävän paljon satunnaisuutta. Tässä ohjelma, joka palauttaa listan ensimmäisistä b:n arvoista jotka palauttavat uniikkeja arvoja. Niille voidaan tehdä sitten pidempi jatkotarkastelu edellisen postin viimeisellä ohjelmalla. (tarkoituksena olisi tietysti löytää kaava tälle riittävän suurelle b:n arvolle, nimä ohjelmat ovat suhteellisen hitaita (kaavassa esimerkiksi b=?puskurin pituus, kellon jonojen pituus?).

struct list {
  struct list *next;
  char *string;
};

struct list *first;

char *filename="test23.txt";
char *procname=NULL;
FILE *fpd;

clearlist(struct list **l)
{
  struct list *l1,*l2;

  l1=*l;

  while(l1!=NULL) {
    free(l1->string);
    l2=l1->next;
    free(l1);
    l1=l2;
  }
  *l=NULL;
}

int addstring(struct list **l, char *string)
{
  struct list *l1,*ln,*llast;

  ln=malloc(sizeof(struct list));
  ln->next=NULL;
  ln->string=malloc(strlen(string)+1);
  strcpy(ln->string,string);

  l1=*l;
  if(l1==NULL) {
    *l=ln;
  } else {
    llast=NULL;
    while(l1!=NULL) {
      llast=l1;
      l1=l1->next;
    }
    if(llast!=NULL)
      llast->next=ln;
    else
      (*l)->next=ln;
  }
}

int findstring(struct list *l,char *string)
{
  int ok;

  ok=1;
  while(l!=NULL) {
    if(!strcmp(l->string,string)) {
      ok=0;
      break;
    }
    l=l->next;
  }
  return(ok);
}

dumpstrings(struct list *l)
{
  while(l!=NULL) {
    fprintf(stdout,"\ndump: %s",l->string);
    l=l->next;
  }
}

int dotest(int testsize, int b,int dumpsize,int testcount)
{
  int c,e,alku,loppu,dup;
  char filename[128];
  unsigned char buffer[32768];
  unsigned char string[32768],cdigit[3];
  FILE *fp1;
  struct list *l;

  l=NULL;

  dup=0;

  alku=(int)time(NULL);
  for(c=0;c<testcountmax*2;c++) { /* tuo kakkonen kaipaa säätämistä.. */
    memset(buffer,0,testsize);
    genbytes(testsize,buffer,b);
    string[0]='\0';
    if(dumpsize>testsize) {
      for(e=0;e<testsize;e++) {
        sprintf(cdigit,"%02x",buffer[e]);
        strcat(string,cdigit);
      }
    } else {
      for(e=0;e<dumpsize;e++) {
        sprintf(cdigit,"%02x",buffer[e]);
        strcat(string,cdigit);
      }
    }
    if(!findstring(l,string)) {
      dup=1;
      fprintf(stdout,", Duplicate");
      fflush(stdout);
      break;
    } else {
      addstring(&l,string);
    }
  }
  }
  loppu=(int)time(NULL);
  fprintf(fpd,"%d testsize, %d bytes per bit, %d total tries, %d testcountmax, %d total seconds, %f seconds per crypt, %d duplicate",
          testsize,b,c,testcountmax,loppu-alku,((double)((double)loppu-alku)/testcount),dup);
  if(dup==0) {
  } else {
    if(testcountmax<c)
      testcountmax=c;
  }
  fprintf(fpd,"\n");
  fflush(fpd);

  clearlist(&l);
  return(dup);
}

#define USE100 2
#define USE50 2
#define USE10 2
main(int argc,char *argv[])
{
  int c,b,last;

  procname=argv[0];

  testcountmax=1024*64;

  if((fpd=fopen(filename,"a"))==NULL) {
    fprintf(stdout,"%s: cannot open file %s\n",procname,filename);
  }

  for(c=16;c<=4096;c+=c) {
    testcountmax=1024*32;
    last=0;
#ifdef USE100
    for(b=last+1;b<1024;b+=100) {
      if(dotest(c,b,23,1048576*8)==0)
        break;
      last=b;
    }
#endif
#ifdef USE50
    for(b=last+1;b<1024;b+=50) {
      if(dotest(c,b,23,1048576*8)==0)
        break;
      last=b;
    }
#endif
#ifdef USE10
    for(b=last+1;b<1024;b+=10) {
      if(dotest(c,b,23,1048576*8)==0)
        break;
      last=b;
    }
#endif
    for(b=last+1;b<1024;b++) {
      if(dotest(c,b,23,1048576*8)==0)
        break;
      last=b;
    }
    fprintf(stdout,"%d testsize, %d bytes per bit\n",c,last);
  }
}

Tässä tuloste Probook laitteella:

16 testsize, 1 bytes per bit, 389 total tries, 32768 testcountmax, 0 total seconds, 0.000000 seconds per crypt, 1 duplicate
16 testsize, 101 bytes per bit, 36442 total tries, 32768 testcountmax, 91 total seconds, 0.000011 seconds per crypt, 1 duplicate
16 testsize, 201 bytes per bit, 72884 total tries, 36442 testcountmax, 370 total seconds, 0.000044 seconds per crypt, 0 duplicate
16 testsize, 102 bytes per bit, 19587 total tries, 36442 testcountmax, 46 total seconds, 0.000005 seconds per crypt, 1 duplicate
16 testsize, 152 bytes per bit, 72884 total tries, 36442 testcountmax, 295 total seconds, 0.000035 seconds per crypt, 0 duplicate
16 testsize, 103 bytes per bit, 72811 total tries, 36442 testcountmax, 219 total seconds, 0.000026 seconds per crypt, 1 duplicate
16 testsize, 113 bytes per bit, 28246 total tries, 72811 testcountmax, 75 total seconds, 0.000009 seconds per crypt, 1 duplicate
16 testsize, 123 bytes per bit, 145622 total tries, 72811 testcountmax, 649 total seconds, 0.000077 seconds per crypt, 0 duplicate
16 testsize, 114 bytes per bit, 145622 total tries, 72811 testcountmax, 630 total seconds, 0.000075 seconds per crypt, 0 duplicate

Ensimmäiset kierrokset kasvatetaan b:tä sadalla. Kun ensimmäinen vain uniikkipuskureita sisältävä rivi tulee vastaan (b=201), peruutetaan viimeiseen duplikaatteja palauttaneeseen riviin(b=101), ja jatketaan 50 välein seuraavasta b:n arvosta. Samoin käydään lääpi sekä kkymmenet ja ykköset. Tässä ensimmäinen potentiaalinen uniikkeja puskureita palauttava rivi on b:n arvolla 114.

Satunnaislukugeneraattorin uusi käsittely

Kirjoitin aiemmin postissa http://moijari.com/?p=62 genbyte ja genbytes rutiineihin perustuvasta satunnaisbittigeneraattorista, joka tekee satunnaislukuja kellon perusteella. Satunnaislukugeneraattori perustuu vaihteluihin, joita tapahtuu luettaessa kelloa toistuvasti. (rutiinipari on oma keksintöni)

HUOM: Tätä ei tällä tiedolla kannata käyttää ainoana satunnaislukugeneraattoreina, vaan kannattaa aina summata useampia generaattoreita xor:aamalla tai tiivistefunktiolla. Tietysti on mukavaa, jos joku summattavista generaattoreista on omassa ohjelmassa…

HUOM2: Ilmeisesti b:n arvo 20 on liian pieni 64 merkin puskurille, se tuottaa duplikaattipuskureita. Kokeiluissani b:n arvolla 100 ei duplikaattipuskureita ole tullut 64 merkkisellä puskurilla (muutamilla miljoonilla ajokerroilla). Tavoite on saada b:n arvo määriteltyä siten, että satunnaisbittien määrä riittää koko puskuriin, ja että kuitenkin ajoaika on kohtuulinen (tuplien laskuohjelma lopussa). Edit: Onko niin, että jos satunnaislukugeneraattori alkaa tuottamaan tuplia ~1048576 suorituskerran jälkeen, inputista (tässä kellodatasta) saadaan ~20 (2^20=1048576) bittiä satunnaisuutta. ja jos satunnaisuutta halutaan 40 bittiä, lisätään kierroksia kaksinkertaiseksi (alkuperäinen b on esim 30, uusi b on 60). Pondering..

summary: this random bit generator is based on fluctuations on bit stream reading clock repeatedly. post tries to show what these fluctuations are, and how they are used to create random bit stream. post gives code that i created in these couple of days i have been studying generator. generator itself was invented by me in ~november 2013. code for the generator is in next two functions, and the rest of the post tries to show how it works. i am not totally confident that the generator is good enough, but confident enough to write this summary in english. language of the post is finnish, of course.

Rutiinit ovat seuraavassa, ensimmäisen rutiinin nimi on muutettu ja “turhat” bitit on otettu pois (millisekunnit, minuutit, tunnit, päivät, kuukaudet… oli aiemmassa versiossa ympätty yhteen merkkiin). Tämä on helpompi selittää ja ymmärtää, siinä on vain millisekuntien alimmat 8 bittiä:

/*
 * (c)2013-2016 Jari Kuivaniemi, Kaikki oikeudet pidätetään!
 */
unsigned char clockbyte()
{
  unsigned char byte;
  unsigned long usec,sec;

  struct timeval tv;

  gettimeofday(&tv,NULL);

  usec=tv.tv_usec;
  sec=tv.tv_sec;

  byte=(usec&0xff);

  return(byte);
}

genbytes(int size, unsigned char *buffer, int b)
{
  int c,d,e,f;
  unsigned char byte;

  f=0;
  for(c=0;c<8*b;c++) {
    for(d=0;d<size;d++) {
      byte=clockbyte();
      e=buffer[d];
      e=((e&0x80)>>7) | ((e&0x7f)<<1);
      e=e^byte;
      buffer[d]=e;
    }
    for(d=0;d<size;d++) { /* see rc4 */
      f=(f+buffer[d])%size;
      e=buffer[d];
      buffer[d]=buffer[f];
      buffer[f]=e;
    }
  }
}

Seuraavilla testirutiineilla voidaan mielestäni havainnollistaa näitä vaihteluita:

test_timer1()
{
  int c,size;
  unsigned char *buffer;

  size=buffer_size*8*bytes_per_bit;

  if((buffer=malloc(size))==NULL) {
    fprintf(stderr,"jarik2: cannot allocate testing buffer");
  }

  for(c=0;c<size;c++) {
    buffer[c]=clockbyte();
  }

  for(c=0;c<size;c++) {
    if(c>0 && c%64==0)
      fprintf(stdout,"\n");
    fprintf(stdout," %3d",buffer[c]);
  }

  free(buffer);
}

Edellisen rutiinin size lauseke sisältää kaikki puskurin laskemiseen tarvittavat merkit, eli siis näemme kaikki merkit, jotka vaikuttavat puskuriin. (buffer size on näissä testeissä 64 ja b eli bytes per bit on esimerkiksi 20)

Seuraavana edellisen rutiinin osittainen tuloste merkki merkiltä. tämä lähinnä kertoo, millaista suoraan kellolta tulevat merkit näyttävät: (huomaa että esimerkiksi 1,3,5,6,8 jne puuttuvat, tuplia tässä ei ole hmm…)

 246 248 251 253 255   2   4   7   9  10  11  12  13  14  15  16  17
  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34
  35  36  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52
  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69
  70  71  72  73  74  75  76  77  78  79  80  81  82  84  85  86  87
  88  89  90  91  92  93  94  95  96  97  98  99 100 101 102 103 104
 105  97  98  99 100 101 102 103 104 105 106 107 108 109 110 111 112
 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
 130 131 132 133 134 135 136 137 139 140 141 142 143 144 145 146 147
 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
 182 183 184 185 186 187 188 189 190 192 193 194 195 196 197 198 199
 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
 234 235 236 237 239 240 241 242 243 244 245 246 247 248 249 250 251
 252 253 254 255   0   1   2   3   4   5                                                                                                                
...

Ohjelma kaksi jakaa tulosteen riveihin, siten että rivin pienin numero aloittaa aina rivin.

test_timer2()
{
  int c,size,start;
  unsigned char *buffer;

  size=buffer_size*8*bytes_per_bit;

  if((buffer=malloc(size))==NULL) {
    fprintf(stderr,"jarik2: cannot allocate testing buffer");
  }
  for(c=0;c<size;c++) {
    buffer[c]=clockbyte();
  }

  start=0;
  /* same as test1 routine except cr in between largest and smallest number.                                                                    
   */
  for(c=0;c<size;c++) {
    if(start==1 && buffer[c]<buffer[c-1])
      fprintf(stdout,"\n");
    fprintf(stdout," %3d",buffer[c]);
    start=1;
  }
  free(buffer);
}

Tämä versio on hiukan muokattu edellisestä(osa tulosteesta). Nolla on aina samalla kohdalla, tässä voi jo vertailla eri jonoja hakemalla puuttuvia ja tuplia. (Puuttuvat ja tuplat antavat generaattorin satunnaisuuden)

   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27                                
  28  29  30  31  32  33  34  35  36  37  38  39  40  41  43  44  45  46  47  48  49  50  51  52  53  54  55  56                                
  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84                                
  85  86  87  88  90  90  92  93  94  95  96  97  98  99 100 101 102 103 104 105 106 107 108 109 110 111 112 113                                
 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 138 139 140 141 142                                
 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170                                
 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 190 191 192 193 194 195 196 197 198 199                                
 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227                                
 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 248 249 250 251 252 253 254 255                                    
   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27                                
  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  44  44  46  47  48  49  50  51  52  53  54  55  56                                
  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84                                
  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 101 102 104 104 106 107 108 109 110 111 112 113                                
 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141                                
 142 143 144 145 146 147 148 149 150 151 154 156 158 161 164 166 168 171 173 176 178 180 183 185 188 190 193 195                                
 197 199 200 201 202 203 204 205 206 207 208 209 210 211 212 214 214 216 217 218 219 220 221 222 223 224 225 226                                
 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254                                
 255
...                                                                                                                                            

Kolmannessa versiossa tulostetaan(osa listasta) merkeistä vain 4 alinta bittiä:

test_timer3()
{
  int c,size;
  unsigned char *buffer;

  size=buffer_size*8*bytes_per_bit;

  if((buffer=malloc(size))==NULL) {
    fprintf(stderr,"jarik2: cannot allocate testing buffer");
  }

  /* Same as previous routine except only low 4 bits printed. */
  for(c=0;c<size;c++) {
    buffer[c]=clockbyte()&0x0f;
  }

  for(c=0;c<size;c++) {
    if(c>0 && c%64==0)
      fprintf(stdout,"\n");
    fprintf(stdout," %2d",buffer[c]);
  }

  free(buffer);
}

 10 13 15  1  4  6  9 11 14  0  2  5  7  9 10 11 12 13 14 15  0  1  2  3  4  5  7  7  9 10 11 12 13 14 15  0  1                                 
  2  3  4  5  6  7  8  9 10 11 12 13 14 15  0  1  2  3  5  6  7  8  9 10 11 12 13 14 15  0  1  2  3  4  5  6  7                                 
  8  9 10 11 12 13 14 15  0  1  2  3  4  5  6  7  8  9 10 11 13 14 15  0  1  2  3  4  5  6  7  8  9 10 11 12 13                                 
 14 15  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14  0  0  2  3  4  5  6  7  8  9 10 11 12 13 14 15  0  1  2  3                                 
  4  5  6  7  8  9 10 11 12 13 14 15  0  1  2  3  4  5  7  8  9 10 11 12 13 14 15  0  1  2  3  4  5  6  7  8  9                                 
 10 11 12 13 14 15  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15  0  1  2  4  5  6  7  8  9 10 11 12 13 14 15                                 
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15  0  2  2  4  5                                 
  6  7  8  9 10 11 12 13 14 15  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15  0  1  2  3  4  5  6  7  8  9 10                                 
 11 12 13 14 15  0  2  3  4  5  6  7  8  9 10 11 12 13 14 15  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15  0                                 
  1  2  3  4  5  6  7  8  9 10 11 12 13 14  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15  0  1  2  3  4  5  6                                 
  7  9 10 12 13 14 15  0  1  2  3  4  5  6
...

Ja neljännessä versiossa vaihdetaan taas riviä numerosasrjojen välissä:

test_timer4()
{
  int c,size,start;
  unsigned char *buffer;

  size=buffer_size*8*bytes_per_bit;

  if((buffer=malloc(size))==NULL) {
    fprintf(stderr,"jarik2: cannot allocate testing buffer");
  }

  for(c=0;c<size;c++) {
    buffer[c]=clockbyte()&0x0f;
  }

  start=0;
  for(c=0;c<size;c++) {
    if(start==1 && buffer[c]<buffer[c-1])
      fprintf(stdout,"\n");
    fprintf(stdout," %2d",buffer[c]);
    start=1;
  }

  free(buffer);
}

Tässä on testin 4 osatuloste. Tässä voidaan selvästi havaita puuttuvat ja kaksois merkit.

  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15                                                                                                
  1  1  3  4  5  6  7  8  9 10 11 12 13 14 15                                                                                                   
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15                                                                                                
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14                                                                                                   
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15                                                                                                
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15                                                                                                
  0  1  2  3  4  5  6  7  8  9 11 11 13 14 15                                                                                                   
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15                                                                                                
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15                                                                                                
  0  1  2  3  4  6  7  8  9 10 11 12 13 14 15                                                                                                   
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15                                                                                                
  0  1  2  3  4  5  6  9 12 14                                                                                                                  
  1  3  6  8 10 13 15                                                                                                                           
  2  4  7  9 12 14                                                                                                                              
  1  3  5  6  7  8  9 10 11 12 13 14 15                                                                                                         
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15                                                                                                
  0  1  2  3  4  6  6  8  9 10 11 12 13 14 15                                                                                                   
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15                                                                                                
  0  1  2  3  4  5  8 10 13 15                                                                                                                  
  2  4  6  9 11 14                                                                                                                              
  0  3  5  7  8  9 10 11 12 13 14 15                                                                                                            
  0  1  2  3  5  7 10 12 15                                                                                                                     
  1  4  6  8 11 13                                                                                                                              
  0  2  4  7  9 12 14
...

Seuraavassa on osatuloste rutiinin pääohjelmasta (genbytes), joka lukee näitä lukuja ja muodostaa puskuriin satunnaisbittijonon:

Tässä ensimmäisessä tietueessa generaattori on aloitustilassa, sillä on tyhjä puskuri, joka on täynnä nollamerkkejä. Todellisuudessa generaattorin alkuarvo voidaan täyttää kutsuvassa ohjelmassa.

genbytes input                                                                                                                                  
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Tässä toisessa tietueessa (genbytes():in ensimmäisen luupin jälkeen) näemme selvästi kellon luvun tuoman rakenteen, merkit alkavat 0xb0 merkistä ja loppuvat 0xdf merkkiin (huomaa puuttuvat b1,b2 ja tuplat b4, b7, bb, be jne). Tässä vielä ensimmäinen luuppi:

for(d=0;d<size;d++) { /* Käydään läpi koko puskuri */
  byte=clockbyte();   /* Luetaan kello */
  e=buffer[d];        /* Luetaan vuorossa oleva merkki */
                      /* Siirretään ylin bitti alas ja 7 alabittiä askel ylös */
  e=((e&0x80)>>7) | ((e&0x7f)<<1);  
  e=e^byte;           /* xorataan kellomerkillä */
  buffer[d]=e;        /* laitetaan takaisin samaan paikkaan puskuriin */
}
genbytes after adding clock bits                                                                                                                
b0b3b4b4b5b6b7b7b8b9b9babbbbbcbdbebebfc0c0c1c2c3c3c4c5c5c6c7c8c8c9cacacbcccdcdcecfcfd0d1d2d2d3d4d5d5d6d7d7d8d9d9dadbdcdcdddededff

Kolmannessa rivissä genbytes rutiinin jälkimmäinen kappale sekoittaa merkkien järjestyksen merkkien perusteella: (merkit ovat vielä väliltä 0xb0-0xdf). Tässä vielä toinen luuppi:

for(d=0;d<size;d++) {   /* Käydään koko puskuri läpi merkki merkiltä */
  f=(f+buffer[d])%size; /* f:ään lisätään puskurin tämänhetkinen merkki mod size */
  e=buffer[d];          /* vaihdetaan d:s(tämänhetkinen) ja f:s merkki */
  buffer[d]=buffer[f];
  buffer[f]=e;
}
genbytes after mixing bytes                                                                                                                     
ddcbb4bac9d9d2c8c6c1bcc3b4dad7d3c4d0b8bfc0c5b7c3dec7becab3bdcadbb7b0d4dcbbd8c2b6c8b9c5d1cfd6d2b9cecdded9d5d7dcbecfdfccbbb5cdd5c0

Huomaa että toisessa tietueessa alin bitti on jonoina nollia ja ykkösiä, mutta kun puskurin “sekoitus” tehdään (kolmannella rivillä) nollien ja ykkösten järjestys muuttuu satunnaiseksi. Puskurin pituuden pitää olle riittävän pitkä (jos aloituspuskuri on tyhjä), ettei puskurin riville tule pelkästään nollia tai ykkösiä. (nollien lukumäärä rivillä tilastollisesti ~ykkösten lukumäärä).

Neljännessä rivissä lisäämme toisen kellorivin tulosteeseen. Huomaa, että kun nyt lisäämme puskuriin uuden rivin sitä ei kirjoiteta samassa järjestyksessä kun edellisellä kierroksella, edellisen kierroksen merkit on sekoitettu satunnaisesti. Sekoituksessa käytetään kellon tuottamia tämän hetkisiä merkkejä (siis puskurin sisältöä).

Huomaa lisäksi että yhden merkin bitit on rollattu ylöspäin, joten kellon tarkin bittit tapaa itseasiassa ensimmäisen rivin ylimmän bitin, ja ensimmäisessä kierroksessa lisätty kellobitti on nyt toisessa bitissä.

genbytes after adding clock bits                                                                                                                
cae51a01e7c6d3e7fafb00fe13ced4dbf4df0f00010bee053e0cf910e2fd123fe7e82333fc3d08e01ffc04330e3f37e0090f28253c3821e4062502ebf706351f

Viidennessä rivissä sekoitamme bitit taas uuteen järjestykseen, ja jatkamme kierroksia, kunnes kellon alin bitti on koskenut kaikkiin puskurin bitteihin.

genbytes after mixing bytes                                                                                                                     
3fe8fc0801fbfee7f7101f06e7122823df3df937023313383c0625ee051ae53e09d3cee035ebc61ff425e4fcd40c0421fd00ca0edbe00b0fe23fe70ffa013300                
genbytes after adding clock bits                                                                                                                
4ae4cf2735cfc4f6d51b0230f21a6e79ff3bb22c472562353e4a0d95427d813759ebd08c2498dd6eb8189baafd4d5d14ac57cd45ed9b4d4299239140aa620761                
genbytes after mixing bytes                                                                                                                     
623723599881ddcf27354a02991ac4d0f2f6b279ac0d5d6e07cf479bfd18aaeb4a61d52c457d30b84d254257ed6e3e421b14ff3b408caacd24e43591954d629b                
genbytes after adding clock bits                                                                                                                
369db247c4f54c68b6936efec8c8755c1b129af2581bb8df0a9b8b31fd375dde9dc8a05386f76d7f955a94bfc9ce6f90233de96197014c8152d2763f3684db28                
genbytes after mixing bytes                                                                                                                     
c49a90ce1bb63f536ec9c80a1b7ffd61f2df3d315a1236819397d29d9df79b76688bbf474cb84cfe3784865c01f56fc82895e952b2db6da0de3675c8235d9458                
genbytes after adding clock bits                                                                                                                
2685902f84deca13692526a38e4741785e03c6df0a9bd3c3e6ee67f8ff2bf22a16d0b74651bb5336a2c4c076cd3b0e4082f90070b0620c966ab4334b9c61f56c                
genbytes after mixing bytes                                                                                                                     
f52ae67640cac3c0d06a7061693b0e90a22b4678d3f84bb02fde008485c4b7a35e26475113bb25b4c6ff8e0c6cee9bf91696035333419c26f2df826267cd360a                
genbytes after adding clock bits                                                                                                                
853bbd9df1e7f4f2d5a196b4a50e64583f2cf78cda8ce81e213d818b890aebc339ca0925aefec3e3067391955653b863bdbc9435f516acda73289d5c5701f78f                
genbytes after mixing bytes                                                                                                                     
910e3f815721e82c732528caf794da89a553da7309955caeebd5f13df71ebd3556f2b4e38fa10a1606acc3859d9d3bbdf596398bf4fe6358bce7c3b864018c8c                
genbytes after adding clock bits                                                                                                                
0e32502c9e73e06ad57964a0da1f822b739f8fdc29178560e994dc3aae7e3929e8a12c8158045c654513cc4077763835a47d2246bbae95e42c9ad126905a4043                
genbytes after mixing bytes                                                                                                                     
7d136a76bb50dc5a26a0354595642b732c04ae1740393a2c909eae82dc0e5c9a77e4e9811f40a4d53879a129d17e43cc6094222c32584665e029858f9fdae873                
genbytes after adding clock bits                                                                                                                
10cc3f009a4d575ba3b19b78d93ba212adfeabd9788b8da2dac6a1f847e24735eec8d1003d844cae76f54b5aaaf68c92cc2449566bbf9cdbd340180b2ba0c7f1                
genbytes after mixing bytes                                                                                                        
c700b13f00785b5a12fe3badf8189a928ba384cca1cce2aa0b5678a24deea2aec82bdb353d768d576b4747f68cbf4cd94b9c9bd9a040f5da24d3d1c610f1ab49                

Edellinen rivi on viimeinen rivi, kuten se olisi tulostettu genbytes ohjelman b arvolla 1. Lisäämällä b:n arvoa voidaan tehdä useampia kahdeksan bitin kierroksia. Itse olen ajatellut, että kellodatassa olisi bitin verran tietoa kahtakymmentä byteä kohti, eli rutiinia ajettaisiin b:n arvolla 20 (Edit: HUOM2). Generaattorissa kaikki luetut kellomerkit vaikuttavat lopullisen puskurilliseen eli jos haluat saada puskurin sisällön ilman sitä (sisältöä) joudut arvaamaan kaikki tuplat ja puuttuvat merkit ajoajankohdan lisäksi.

Edellinen esimerkki on laadittu melko hitaalla laitteella (raspberry pi). Nopeammalla laitteella tulee enemmän tuplia ja vähemmän puuttuvia. Itseasiassa tässä on vielä lopuksi testi nopeampaa laitetta varten:

test_timer5()
{
  int c,size,lastbits,countbits;
  unsigned char *buffer;

  size=buffer_size*8*bytes_per_bit;

  if((buffer=malloc(size))==NULL) {
    fprintf(stderr,"jarik2: cannot allocate testing buffer");
  }

  for(c=0;c<size;c++) {
    buffer[c]=clockbyte()&0xf;
  }

  lastbits=-1;
  for(c=0;c<size;c++) {
    if(lastbits==-1) {
      lastbits=buffer[c];
      countbits=1;
    } else if(lastbits!=buffer[c]) {
      fprintf(stdout," %d(%d)",countbits,lastbits);
      if(lastbits>buffer[c]) {
        fprintf(stdout,"\n");
      }
      lastbits=buffer[c];
      countbits=1;
    } else if(lastbits==buffer[c]) {
      countbits++;
    }
  }
  if(c>0)
    fprintf(stdout," %d(%d)",countbits,lastbits);
  fprintf(stdout,"\n");

  free(buffer);
}

Seuraavassa testiohjelman 5 osatuloste: se tulostaa jokaisesta lukemasta määrän ja numeron, esimerkiksi 2(3) tarkoittaa kaksi kappaletta arvoa 3. Erillisten arvojoukkojen välissä on cr.

 1(0) 1(1) 1(2) 1(3) 1(4) 1(5) 1(6) 1(7) 1(8) 1(9) 1(10) 1(11) 1(12) 1(13) 1(14) 1(15)                                                          
 1(0) 1(1) 1(2) 1(3) 1(4) 1(5) 1(6) 1(7) 1(8) 1(9) 1(10) 1(11) 1(12) 1(13) 1(14) 1(15)                                                          
 1(0) 1(1) 1(2) 1(4) 1(5) 1(6) 1(7) 1(8) 1(9) 1(10) 1(11) 1(12) 1(13) 1(14) 1(15)                                                               
 1(0) 1(1) 1(2) 1(3) 1(4) 1(5) 1(6) 1(8) 1(9) 1(10) 1(11) 1(12) 1(13) 1(14) 1(15)                                                               
 1(0) 1(1) 1(2) 1(3) 1(4) 1(5) 1(6) 1(7) 1(8) 1(9) 1(10) 1(11) 1(12) 1(13) 1(14) 1(15)                                                          
 1(0) 1(1) 1(2) 1(3) 1(4) 1(5) 1(7) 1(8) 1(9) 1(10) 1(11) 1(12) 1(13) 1(14) 1(15)                                                               
 1(0) 1(2) 1(5) 1(7) 1(10) 1(12) 1(15)                                                                                                          
 1(1) 1(4) 1(6) 1(8) 1(11) 1(13)                                                                                                                
 1(0) 1(2) 1(5) 1(7) 1(10) 1(12) 1(14) 1(15)                                                                                                    
 1(1) 1(2) 1(3) 1(6) 1(8) 1(11) 1(13)                                                                                                           
 1(0) 1(2) 1(4) 1(7) 1(9) 1(12) 1(14)                                                                                                           
 1(1) 1(3) 1(6) 1(8) 1(11) 1(13)                                                                                                                
 1(0) 1(1) 1(2) 1(3) 1(4) 1(5) 1(8) 1(10) 1(13) 1(15)                                                                                           
 1(2) 1(4) 1(6) 1(9) 1(11) 1(14)                                                                                                                
 1(0) 1(3) 1(5) 1(7) 1(10) 1(12) 1(15)                                                                                                          
 1(1) 1(2) 1(3) 1(4) 1(5) 1(6) 1(7) 1(8) 1(9) 1(10) 1(11) 1(12) 1(13) 1(14) 1(15)                                                               
 1(1) 1(3) 1(6) 1(8) 1(11) 1(13)                                                                                                                
 1(0) 1(2) 1(4) 1(7) 1(9) 1(11) 1(14)                                                                                                           
 1(1) 1(3) 1(6) 1(8) 1(10) 1(13) 1(14) 1(15)                                                                                                    
 1(0) 1(1) 1(2) 1(3) 1(4) 1(5) 1(6) 1(7) 1(8) 1(9) 1(10) 1(11) 1(12) 1(15)                                                                      
 1(1) 1(3) 1(6) 1(8) 1(10) 1(13)                                                                                                                
 1(0) 1(2) 1(5) 1(7) 1(9) 1(12) 1(14)
...

Edit: Vielä yksi juttu: seuraava ohjelma laskee samaa merkkiä sisältävien merkkien määrän jonojen pituuksittain:

#define RUNSMAX 50

int runs8[RUNSMAX];

reset_runs8()
{
  int c;

  for(c=0;c<RUNSMAX;c++) {
    runs8[c]=0;
  }
}

test_runs8(int size, unsigned char *buffer)
{
  int c,d,firstbyte,count,total;

  firstbyte=-1;
  count=0;
  for(c=0;c<size;c++) {
    if(firstbyte==-1) {
      firstbyte=buffer[c];
      count=1;
    } else {
      if(buffer[c]==firstbyte) {
        count++;
      } else {
        if(count>=RUNSMAX)
          count=RUNSMAX-1;
        if(count<RUNSMAX) {
          runs8[count]++;
        }
        firstbyte=buffer[c];
        count=1;
      }
    }
  }
  if(count>=RUNSMAX)
    count=RUNSMAX-1;
  if(count<RUNSMAX) {
    runs8[count]++;
  }
}

results_runs8()
{
  int c,d,total;
  char tmp[32],buffer[4096];

  total=0;

  *buffer='\0';
  for(d=RUNSMAX-1;d>0 && runs8[d]==0;d--);
  for(c=1;c<=d;c++) {
    sprintf(tmp," %d:%d",c,runs8[c]);
    strcat(buffer,tmp);
    total+=c*runs8[c];
  }
  message("runs8: any: %s",buffer);
  message("runs8: total:  %d",total);
}

Se tulostaa (hiukan nopeammalla koneella) seuraavan listan. Listan perusteella kellodatassa on eniten neljän ja viiden merkin pituisia samaa merkkiä sisältäviä sarjoja eli ne ovat ilmeisesti perusjono ja 1, 2, 3 ja 6 merkin sarjoja voidaan pitää satunnaisuutena.

runs8: any:  1:139 2:145 3:128 4:1292 5:2841 6:49
runs8: total:  20480

Näyte datasta (test_timer1())

 237 237 237 237 238 238 238 238 238 239 239 239 239 240 240 240 240 240
 241 241 241 241 242 242 242 242 242 243 243 243 243 244 244 244 244 244
 245 245 245 245 246 246 246 246 246 247 247 247 247 248 248 248 248 248
 249 249 249 249 250 250 250 250 250 251 251 251 251 252 252 252 252 252
 253 253 253 253 254 254 254 254 254 255 255 255 255   0   0   0   0   0
   1   1   1   1   2   2   2   2   2   3   3   3   3   4   4   4   4   4
   5   5   5   5   5   6   6   6   6   7   7   7   7   7   8   8   8   8
   8   9   9   9   9  10  10  10  10  10  11  11  11  11  11  12  12  12
  12  13  13  13  13  13  14  14  14  14  15  15  15  15  15  16  16  16
  16  17  17  17  17  17  18  18  18  18  19  19  19  19  19  20  20  20
  20  21  21  21  21  21  22  22  22  22  23  23  23  23  23  24  24  24
  24  25  25  25  25  25  26  26  26  26  27  27  27  27  27  28  28  28
  28  29  29  29  29  29  30  30  30  30  31  31  31  31  31  32  32  32
  32  33  33  33  33  33  34  34  34  34  35  35  35  35  35  36  36  36
  36  37  37  37  37  37  38  38  38  38  39  39  39  39  39  40  40  40
  40  40  41  41  41  41  42  42  42  42  42  43  43  43
...

Tuloste raportilla test_timer5(): En kyllä nelosen ja viitosen vaihteluitakaan lähtisi arvaamaan..

 5(0) 5(1) 4(2) 5(3) 5(4) 4(5) 5(6) 5(7) 4(8) 5(9) 5(10) 4(11) 5(12) 5(13) 4(14) 5(15)
 5(0) 4(1) 5(2) 5(3) 5(4) 4(5) 5(6) 5(7) 4(8) 5(9) 5(10) 5(11) 4(12) 5(13) 5(14) 4(15)
 5(0) 5(1) 5(2) 4(3) 5(4) 5(5) 4(6) 5(7) 4(8) 5(9) 5(10) 4(11) 5(12) 5(13) 4(14) 5(15)
 5(0) 5(1) 4(2) 5(3) 5(4) 4(5) 5(6) 5(7) 4(8) 5(9) 4(10) 5(11) 5(12) 5(13) 4(14) 5(15)
 5(0) 4(1) 5(2) 5(3) 5(4) 4(5) 5(6) 5(7) 5(8) 4(9) 5(10) 5(11) 4(12) 5(13) 5(14) 4(15)
 5(0) 5(1) 4(2) 5(3) 5(4) 5(5) 4(6) 5(7) 5(8) 4(9) 5(10) 5(11) 4(12) 5(13) 5(14) 5(15)
 4(0) 5(1) 5(2) 5(3) 4(4) 5(5) 4(6) 5(7) 5(8) 4(9) 5(10) 5(11) 5(12) 4(13) 5(14) 5(15)
 5(0) 4(1) 5(2) 5(3) 4(4) 5(5) 5(6) 4(7) 5(8) 5(9) 4(10) 5(11) 5(12) 4(13) 5(14) 5(15)
 4(0) 5(1) 5(2) 5(3) 4(4) 5(5) 5(6) 4(7) 5(8) 5(9) 5(10) 5(11) 5(12) 5(13) 5(14) 5(15)
 5(0) 6(1) 5(2) 5(3) 5(4) 5(5) 5(6) 4(7) 6(8) 5(9) 5(10) 5(11) 5(12) 5(13) 5(14) 5(15)
 6(0) 4(1) 5(2) 5(3) 6(4) 5(5) 4(6) 5(7) 5(8) 4(9) 5(10) 5(11) 4(12) 5(13) 5(14) 5(15)
 4(0) 5(1) 5(2) 4(3) 5(4) 5(5) 4(6) 5(7) 5(8) 4(9) 5(10) 5(11) 4(12) 5(13) 5(14) 4(15)
 5(0) 4(1) 5(2) 5(3) 4(4) 5(5) 5(6) 4(7) 5(8) 5(9) 4(10) 5(11) 5(12) 4(13) 5(14) 5(15)
 4(0) 5(1) 5(2) 4(3) 5(4) 5(5) 5(6) 4(7) 5(8) 5(9) 4(10) 5(11) 4(12) 5(13) 5(14) 5(15)
 4(0) 5(1) 5(2) 4(3) 5(4) 5(5) 4(6) 5(7) 5(8) 5(9) 4(10) 5(11) 5(12) 4(13) 5(14) 5(15)
 4(0) 5(1) 4(2) 5(3) 5(4) 4(5) 5(6) 5(7) 5(8) 4(9) 5(10) 5(11) 4(12) 5(13) 5(14) 4(15)
 5(0) 5(1) 4(2) 5(3) 5(4) 4(5) 5(6) 5(7) 5(8) 5(9) 4(10) 5(11) 5(12) 4(13) 5(14) 4(15)
...

Edit: vielä tilastotietoa genbytes():in tulostamasta merkkijonosta ja tilastot luovat ohjelmat: (puskurin koko on 2500 merkkiä, kuten FIPS-120 suosittelee (osa testeistä on laadittu näiden satunnaisuus testien mukaan))

monobit ones: 9994 zeroes 10006, total: 20000
poker2:  data:  0:2505 1:2485 2:2499 3:2511
poker2: total: 10000, lowest: 2485, highest: 2511
poker4:  data:  0:327 1:315 2:280 3:309 4:283 5:287 6:345 7:319 8:339 9:311 10:305 11:287 12:325 13:338 14:327 15:303
poker4: total: 5000, lowest: 280, highest: 345
runs: stat:    1:2500 2:1250 3:625 4:312 5:156 6:78 7:39 8:19 9:9 10:4 11:2 12:1 13:0 14:0 15:0
runs: zeroes:  1:2448 2:1227 3:611 4:336 5:158 6:75 7:47 8:22 9:5 10:10 11:0 12:1 13:1
runs: ones:    1:2410 2:1280 3:615 4:319 5:164 6:76 7:33 8:23 9:13 10:2 11:2 12:2 13:2 14:0 15:1
runs: total:  20000
runs8: any:  1:2482 2:9
runs8: total:  2500

Nämä tilastoluvut kertovat tässä tapauksessa lähinnä satunnaisbittien keräilyn ja sekoittamisen laadusta. Kokeile tulostaa tilastot siten että genbytes():in kellona käytetään vuorotellen lukuja 1-255, kun numerosarja loppuu se aloitetaan taas alusta. Sekin saa varsin hyvät tilastoluvut (ilmeisesti, en kokeillut…). Jos kellon alimmassa bitissä on riittävästi nollia ja ykkösiä, se saa hyvän arvosanan. Edellisessä joka toinen alin bitti on nolla, ja joka toinen 1. Jos teit itsellesi viritetyn kellon, voit jatkaa kokeiluja toistamalla numerot kahteen kertaan ja kolmeen kertaan, ja siitä pääset mukavasti kehittämään kellon arvaamista. Onkohan se niin, että jokaisessa kellon arvon vaihdoksessa on 1 bitti satunnaisuutta? (Jos kello ei ole säännöllinen sarja)

Monobit alkuinen rivi laskee puskurin nollat ja ykköset. Nollia ja ykkösiä pitäisi olla tilastollisesti yhtä paljon.

int monobit_zeroes = 0, monobit_ones = 0;

reset_monobit()
{
  monobit_zeroes = 0;
  monobit_ones = 0;
}

test_monobit(int size, unsigned char *buffer)
{
  int c,d,zeroes,ones;

  for(c=0;c<size;c++) {
    for(d=0;d<8;d++) {
      if((buffer[c]>>d)&1)
        monobit_ones++;
      else
        monobit_zeroes++;
    }
  }
}

results_monobit()
{
  message("monobit ones: %d zeroes %d, total: %d",monobit_zeroes,monobit_ones,monobit_zeroes+monobit_ones);
}

Poker2 testi jakaa puskurin 2 bitin lukuihin, ja tulostaa näiden lukumäärät. (neljä lukua)

int poker2_i[4];

reset_poker2()
{
  int c;

  for(c=0;c<4;c++) {
    poker2_i[c]=0;
  }
}

test_poker2(int size, unsigned char *buffer)
{
  int c;

  for(c=0;c<size;c++) {
    poker2_i[(buffer[c]>>6)&0x03]++;
    poker2_i[(buffer[c]>>4)&0x03]++;
    poker2_i[(buffer[c]>>2)&0x03]++;
    poker2_i[buffer[c]&0x03]++;
  }
}

results_poker2()
{
  int c,total,lowest,highest;
  char tmp[32],buffer[1024];

  total=0;
  lowest=999999999;
  highest=0;
  *buffer='\0';
  for(c=0;c<4;c++) {
    sprintf(tmp," %d:%d",c,poker2_i[c]);
    strcat(buffer,tmp);

    total+=poker2_i[c];
    if(lowest>poker2_i[c])
      lowest=poker2_i[c];
    if(highest<poker2_i[c])
       highest=poker2_i[c];
  }
  message("poker2:  data: %s",buffer);
  message("poker2: total: %d, lowest: %d, highest: %d",total,lowest,highest);
}

Poker 4 jakaa puskurin 4 bitin lukuihin ja laskee niiden lukumäärän. Poker4 ei mielestäniei ollut FIPS:issä.

int poker4_i[16];

reset_poker4()
{
  int c;

  for(c=0;c<16;c++) {
    poker4_i[c]=0;
  }
}


test_poker4(int size, unsigned char *buffer)
{
  int c;

  for(c=0;c<size;c++) {
    poker4_i[(buffer[c]>>4)&0x0f]++;
    poker4_i[buffer[c]&0x0f]++;
  }
}

results_poker4()
{
  int c, total, lowest, highest;
  char tmp[32],buffer[1024];

  total=0;
  lowest=999999999;
  highest=0;
  *buffer='\0';
  for(c=0;c<16;c++) {
    sprintf(tmp," %d:%d",c,poker4_i[c]);
    strcat(buffer,tmp);
    total+=poker4_i[c];
    if(lowest>poker4_i[c])
      lowest=poker4_i[c];
    if(highest<poker4_i[c])
      highest=poker4_i[c];
  }
  message("poker4:  data: %s",buffer);
  message("poker4: total: %d, lowest: %d, highest: %d",total,lowest,highest);
}

Seuraava testi on runs, ja se laskee peräkkäisten 0 ja 1 jonojen määrän puskurissa.

#define RUNSMAX 50

int runs_zero[RUNSMAX];
int runs_ones[RUNSMAX];

reset_runs()
{
  int c;

  for(c=0;c<RUNSMAX;c++) {
    runs_zero[c]=0;
    runs_ones[c]=0;
  }
}

test_runs(int size, unsigned char *buffer)
{
  int c,d,firstbit,count,total;

  firstbit=-1;
  count=0;
  for(c=0;c<size;c++) {
    for(d=0;d<8;d++) {
      if(firstbit==-1) {
        firstbit=buffer[c]&1;
        count=1;
      } else {
        if(((buffer[c]>>d)&1)==firstbit) {
          count++;
        } else {
          if(count>=RUNSMAX)
            count=RUNSMAX-1;
          if(count<RUNSMAX) {
            if(firstbit==0)
              runs_zero[count]++;
            else
              runs_ones[count]++;
          }
          firstbit=(buffer[c]>>d)&1;
          count=1;
        }
      }
    }
  }
  if(count>=RUNSMAX)
    count=RUNSMAX-1;
  if(count<RUNSMAX) {
    if(firstbit==0)
      runs_zero[count]++;
    else
      runs_ones[count]++;
  }
}

results_runs(int size)
{
  int c,d,total;
  char tmp[32],buffer[4096];

  total=0;

  *buffer='\0';
  for(d=RUNSMAX-1;d>0 && runs_zero[d]==0 && runs_ones[d]==0 ;d--);
  for(c=1;c<=d;c++) {
    sprintf(tmp," %d:%d",c,(int)(pow((double)0.5,c+2)*(size*8)));
    strcat(buffer,tmp);
  }
  message("runs: stat:   %s",buffer);

  *buffer='\0';
  for(d=RUNSMAX-1;d>0 && runs_zero[d]==0;d--);
  for(c=1;c<=d;c++) {
    sprintf(tmp," %d:%d",c,runs_zero[c]);
    strcat(buffer,tmp);
    total+=c*runs_zero[c];
  }
  message("runs: zeroes: %s",buffer);

  *buffer='\0';
  for(d=RUNSMAX-1;d>0 && runs_ones[d]==0;d--);
  for(c=1;c<=d;c++) {
    sprintf(tmp," %d:%d",c,runs_ones[c]);
    strcat(buffer,tmp);
    total+=c*runs_ones[c];
  }
  message("runs: ones:   %s",buffer);

  message("runs: total:  %d",total);
}

Seuraava testi on runs8, ja se laskee samaa lukua sisältävien merkkijonojen määrän puskurissa. (tämä ei ollut muistaakseni FIPS testeissä). Runs8 testi on listattu aiemmin tässä postissa.

FIPS:issä näissä testeissä oli hylkäämisehto, mutta en vielä ole lisännyt sitä näihin rutiineihin.

Edit 20160120: Vielä duplikaattien laskemiseen liittyvä rutiini:

Duplikaattien lasku:

$ sort test8_64_45_30.txt | uniq -cd

tai

$ sort test8_64_45_30.txt | uniq -d | wc -l
test_timer8()
{
  int c,e,f;
  unsigned char buffer[1024],filename[128];
  FILE *fp1;
  int alku,loppu,b;
  int testcount=16384;
  int testsize=64,dumpsize=30;

  for(;;) {
    for(b=45;b<70;b+=5) {
      for(testsize=64;testsize<=128;testsize+=64) {
        sprintf(filename,"test8_%d_%d_%d.txt",testsize,b,dumpsize);

        fp1=fopen(filename,"a");
        alku=(int)time(NULL);
        for(c=0;c<testcount;c++) {
          memset(buffer,0,testsize);
          genbytes(testsize,buffer,b);
          for(e=0;e<dumpsize;e++)
            fprintf(fp1,"%02x",buffer[e]);
          fprintf(fp1,"\n");
          fflush(fp1);
        }
        fclose(fp1);
        loppu=(int)time(NULL);
        fprintf(stdout,"%d bytes per bit, %d testsize, %d total seconds, %f seconds per crypt\n",
          b,testsize,loppu-alku,((double)((double)loppu-alku)/testcount));
        fflush(stdout);
      }
    }
  }
}
;

Prosessit jatkoa

Kaikki oikeudet tietenkin pidätetään. Viimeinen versio ohjelmasta löytyy seuraavasta linkistä: moijari.com:5002

Olen lisännyt koodia save rutiiniin. Save rutiinin tehtävä on tallettaa lomakkeella tehdyt muutokset. Lisäksi save hoitaa prosessien jatkamisen, eli esimerkiksi lelusovelluksessa tilauksen toimitukseen ja laskutukseen. Save antaa myös talletettaville tietueille avaintiedot, ja määrittelee vierasavainten perusteella haettavat kentät. Save jakaa myös tarvittaessa yhden tiedon kahdeksi (esimerkiksi tilauksessa tilataan tuotteita kahdesta varastosta, ja varastoille tehdään erilliset toimitukset(eli varasto on toimituksen otsaketiedoissa)).

Voit katsoa lelusovelluksessa saven lokkaantumalla sisään, ja menemällä tilausten käsittelynäytölle, painamalla change ja syöttämällä tilauksen kenttiin tietoja (esimerkiksi asiakasnumero 1000, tuotenumetot 3000,3001,3002). Näytön loppuun tulostuu changes if saved kappale, joka listaa muutokset, jotka lomake tällä hetkellä tekee.

Vielä puuttuvat ainakin

  • Avainten arvojen haku
  • Tämän hetkisten tietojen haku
  • Summausta
  • Kaavat kuten ‘rivin summa’=’tilattu määrä’*’tilauksen tuotteen hinta’
  • Tietojen varsinainen lisääminen terttuun
  • Tietojen kenttien oikeellisuuden tarkistus
  • Ehto prosessissa eteenpäin siirtymiseen (esimerkiksi ’tilauksen toimituspäivä’=’kuluvapäivä’ jne.)
  • Tällä hetkellä rivit on yhdistelty siten, että otsake ja rivit on yhdistelty samalle riville, näiden jakaminen otsake ja rivi tietueiksi.

Avainten arvojen haku tarkoittaa esimerkiksi lelusovelluksessa tilausnumeron, toimitusnumeron haku. Tilausnumert on lelusovelluksessa 4000sta lähtien, toimitusnumerot 5000:sta alkaen.

Tämän hetkisten tietojen haku tarkoittaa kaikkien tätä tietoa koskevien tietojen hakua, aiemmat tiedot vaikuttavat tietysti tietueen tekemiin muutoksiin. Esimerkiksi tilaus on voinut aiheuttaa toimituksen, ja määrien muutos vaikuttaa toimituksen määriin, ei välttämättä lisää uutta toimitusta, esimerkiksi.

Summauksesta esimerkkinä voisi olla tilauksen laskutettu määrä kenttä, johon pitää summata kaikki tilausriviä koskevien laskujen laskutettu määrä kentät.

Muistutukseksi vielä ohjelman pitäisi toimia millä tahansa tietueilla, voit keksiä sopivat prosessit muilta toimialoilta, tai tietotarpeista.

Lisäsin loppuun pari koodin pätkää. 2set elements muistuttaa paljon foreign key rutiinia.

Koodailen tätä juuri, koodiin ja postiin saattaa tulla muutoksia.

sovellus_get_elements(char **bones2,char *bones)
{
 int comp;
 char temps[1024],memberid[64];
 struct set *setmembers, *setm;

 setmembers=NULL;
 skk_fetch_sets(&setmembers,bones,skk->first);
 setm=setmembers;
 while(setm!=NULL) {
 set_get_element("memberid",&comp,memberid,setm->data);
 set_add_element(bones2,memberid);
 setm=setm->next;
 }
}

sovellus_get_2set_elements(char **movebones, char *tosovellus, char *fromsovellus)
{
 int c,comp,longest,rest;
 char temps[1024],allname[64],fromname[64],toname[64],savefromname[64],savetoname[64],value[64];
 char *allbones,*pab,
 *frombones,*pfb,
 *tobones,*ptb;

 sprintf(temps,"'memberid'");
 allbones=NULL;
 sovellus_get_elements(&allbones,temps);
 fprintf(stdout,"\nAB: %s",allbones);

 sprintf(temps,"'sovellus'=\"%s\", 'memberid'",fromsovellus);
 frombones=NULL;
 sovellus_get_elements(&frombones,temps);
 fprintf(stdout,"\nFB: %s",frombones);

 sprintf(temps,"'sovellus'=\"%s\", 'memberid'",tosovellus);
 tobones=NULL;
 sovellus_get_elements(&tobones,temps);
 fprintf(stdout,"\nTB: %s",tobones);
  fprintf(stdout,"\nCB: ",tobones);

  *movebones=NULL;
  rest=0;
  pfb=frombones;
  while(*pfb!='\0') {
    set_get_next_element(fromname,&comp,value,&pfb);
    ptb=tobones;
    longest=0;
    while(*ptb!='\0') {
      set_get_next_element(toname,&comp,value,&ptb);
      pab=allbones;
      while(*pab!='\0') {
        set_get_next_element(allname,&comp,value,&pab);
        if( (strstr(fromname,allname)!=NULL) &&
            (strstr(toname,allname)!=NULL) ) {
          if((c=strlen(allname))>longest) {
            longest=c;
            strcpy(savefromname,fromname);
            strcpy(savetoname,toname);
          }
        }
      }
    }
    if(longest>0) {
      sprintf(temps,"'%s' = '%s'",savetoname,savefromname);
      set_add_element_noquotes(movebones,temps);
    }
  }
  fprintf(stdout,"\nCB2: %s",*movebones);
}

Muutama pikku muutos ja yksi suurempi

Kaikki oikeudet tietenkin pidätetään. Viimeinen versio ohjelmasta löytyy seuraavasta linkistä: moijari.com:5002

Terttu-valikossa on uutena valintana yhteenveto, jolla saa koko terttu kannan kuvan. Siitä on helpompi hahmottaa uutta fetch versiota (lukee lokeja). edit: Ja toisaalta sen avulla on helpompi hahmottaa save toimintoa, joka ilmeisesti tekee kaikki tapahtuman tarvitsemat muutokset kantaan (eli nuo tietovirrat, summattujen tietojen päivitykset jne) (vrt. transaktiot, commit). endedit. Esimerkki kannassa se näyttää tälläisen raportin:

Lisäksi poistin submit nappulan login näytöstä, se on nyt muotoa:

html_printf(“<input type=\”submit\” value=\”Submit\” style=\”visibility:hidden;\”>”);

Lisäksi muutos moodi näyttää vain yhden tyhjän rivin entisen 10 sijasta.

Tämä on varmaan lyhin posti, jonka olen kirjoittanut.

 'memberid', 'memberlength', 'membertype'
 'userid', 'username', 'password'
 'sovellus', 'sovelluksen nimi', 'link'
 'sovellus', 'chapter', 'memberid'
 'asiakasnumero', 'asiakkaan nimi', 'asiakkaan osoite', 'asiakkaan postinumero', 'asiakkaan postitmp'
 'toimittajanumero', 'toimittajan nimi', 'toimittajan osoite', 'toimittajan postinumero', 'toimittajan postitmp'
 'tuotenumero', 'tuotteen nimi', 'tuotteen hinta'
 'sovellus', 'fromsovellus', 'tosovellus'
 'tilausnumero', 'tilauksen asiakasnumero', 'tilauksen asiakkaan nimi', 'tilauspäivä', 'tilauksen summa'
 'tilausnumero', 'tilausrivin numero', 'tilauksen tuotenumero', 'tilauksen tuotteen nimi', 'tilauksen tuotteen hinta', 'tilattu määrä', 'rivin summa'
 'tilausnumero', 'asiakasnumero', 'tilauspäivä', 'tilauksen summa'
 'toimitusnumero', 'toimituksen asiakasnumero', 'toimituksen tilauspäivä', 'toimituspäivä', 'toimituksen summa'
 'toimitusnumero', 'toimitusrivin numero', 'toimituksen tuotenumero', 'toimituksen tuotteen nimi', 'toimituksen tuotteen hinta', 'tilattu määrä', 'toimitettu määrä', 'rivin summa'
 'laskunumero', 'asiakasnumero', 'laskun päivä', 'tilauspäivä', 'toimituspäivä', 'eräpäivä', 'laskun summa'
 'laskunumero', 'laskurivin numero', 'tuotenumero', 'tuotteen nimi', 'tuotteen hinta', 'tilattu määrä', 'toimitettu määrä', 'laskutettu määrä', 'rivin summa'
 'tapahtumanumero', 'kirjauspäivä', 'kirjauskuukausi', 'kirjausvuosi', 'laskunumero', 'asiakasnumero', 'tilausnumero', 'toimitusnumero', 'tapahtuman summa'
 'tapahtumanumero', 'tapahtumarivin numero', 'tilinumero', 'tapahtumarivin summa'
 'sovellus', 'chapter', 'set'
 'set', 'memberid'
 'tilinumero', 'tilin nimi'
 'sovellus', 'sovelluksen nimi', 'link', ''
 'asiakasnumero', 'vuosi', 'myynti'
 'asiakasnumero', 'myynti'
 'asiakasnumero', 'tuotenumero', 'myynti'
 'asiakasnumero', 'vuosi', 'tuotenumero', 'myynti'
; Tämä suoritetaan aina save nappulalla, näin saadaan kannasta
; ylläpidettyä ajankohtainen kuva. Huomasin muuten taas pikku optimoinnin,
; etsipä se..

skk_save_overview(struct set *set) /* JariK 20151025 */
{
  int comp,found,add,counts,counto;
  char *p,name[64],value[64],name2[64],value2[64];
  char *bones,temps[64];
  struct set *seto;

  p=set->data;
  bones=NULL;
  while(*p!='\0') {
    set_get_next_element(name,&comp,value,&p);
    sprintf(temps,"'%s'",name);
    set_add_element_noquotes(&bones,temps);
  }
  ; Selataan edellinen overview läpi tietue tietueelta
  ; Jos kenttään add jää tosi, tietue on uusi.
  add=1;
  seto=setoverview;
  while(seto!=NULL) {

    ; Lasketaan overview rivin kenttien lukumäärä
    counto=0;
    p=seto->data;
    while(*p!='\0') {
      set_get_next_element(name,&comp,value,&p);
      counto++;
    }

    ; Lasketaan uuden tietueen kenttien lukumäärä

    counts=0;
    p=set->data;
    while(*p!='\0') {
      set_get_next_element(name,&comp,value,&p);
      counts++;
    }

    ; Jos kenttiä yhtä monta, verrataan kenttä kentält
    if(counto==counts) {

      ; Jos found kenttään jää yksi, löysimme edellisestä overviewistä
      ; tämän tietueen.
      found=1;
      p=seto->data;
      while(*p!='\0') {
        set_get_next_element(name,&comp,value,&p);
        if(set_get_element(name,&comp,value2,bones)==0) {
          found=0;
        }
      }

      ; Jos overview:stä löytyi jo samanmuotoinen tietue,
      ; ei tarvitse lisätä tätä tietuetta sinne.
      if(found==1) {
        add=0;
        break;
      }
    }
    ; Tarkistetaan seuraava overview tietuemalli.
    seto=seto->next;
  }
  ; Jos uusi, lisätään
  ;
  if(add==1) {
    set_add_set_end(&setoverview,bones);
  }
}