/*
 * blockparam.c
 *
 * Given parameters for NASD fs blocks, figure out
 * indirections, sizes, etc
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */

#include <nasd/nasd_options.h>
#include <stdio.h>
#include <nasd/nasd_types.h>

#define BLOCKSIZE  8192 /* block size */

#define ILEVELS 5

nasd_int64 blocks_pointed[ILEVELS], pointers[ILEVELS], bytes_pointed[ILEVELS];
nasd_int64 init_bytes, node_pointers, spare_bytes, max_len, node_pointers0;
nasd_int64 need_bytes;
int psize0, psize, blockptrs0, blockptrs;

#ifndef NASD_CURD
#define NASD_CURD "unknown_dir"
#endif /* !NASD_CURD */

nasd_int64
count_lvls(
  nasd_int64  in_off)
{
  nasd_int64 off;
  int i;

  off = in_off;
  for(i=0;i<ILEVELS;i++) {
    if (bytes_pointed[i] > off)
      return(i);
    off -= bytes_pointed[i];
  }
  return(-1);
}

void
out_len(
  char  *str,
  nasd_int64   len)
{
  nasd_int64 lvls;

  lvls = count_lvls(len);
  if (lvls >= 0) {
    printf(" * Indirect levels for %s coverage: %" NASD_64s_FMT "\n", str, 
      count_lvls(len));
  }
  else {
    printf(" * %s coverage exceeds pointing capacity\n", str);
  }
}

int
main()
{
  nasd_int64 sum, prod;
  nasd_uint64 offset;
  int i, j;

restart:
  printf("Enter direct pointer size ==> ");
  fflush(stdout);
  psize0 = 0;
  scanf("%d", &psize0);
  if (psize0 <= 0) {
    printf("\n");
    fflush(stdout);
    exit(0);
  }

  printf("Enter indirect pointer size ==> ");
  fflush(stdout);
  psize = 0;
  scanf("%d", &psize);
  if (psize <= 0) {
    printf("\n");
    fflush(stdout);
    exit(0);
  }

  printf("Enter bytes in NASD node available for pointers ==> ");
  fflush(stdout);
  init_bytes = 0;
  scanf("%" NASD_64s_FMT, &init_bytes);
  if (init_bytes <= 0) {
    printf("\n");
    fflush(stdout);
    exit(0);
  }

  blockptrs0 = BLOCKSIZE / psize0;
  blockptrs = BLOCKSIZE / psize;

  node_pointers0 = 0;
  node_pointers = 0;
  for(i=0;i<ILEVELS;i++) {
    printf("Enter number of pointers at level %d ==> ", i);
    fflush(stdout);
    pointers[i] = 0;
    scanf("%" NASD_64s_FMT, &pointers[i]);
    if (pointers[i] <= 0) {
      printf("\n");
      fflush(stdout);
      exit(0);
    }
    if (i)
      node_pointers += pointers[i];
    else
      node_pointers0 += pointers[i];
  }
  need_bytes = (node_pointers * psize) + (node_pointers0 * psize0);
  if (need_bytes > init_bytes) {
    fprintf(stderr,
      "ERROR: Cannot fit %" NASD_64s_FMT " indirect and %" NASD_64s_FMT " direct pointers in %" NASD_64s_FMT " bytes (%" NASD_64s_FMT " more bytes needed)\n",
      node_pointers, node_pointers0, init_bytes, need_bytes - init_bytes);
    fflush(stderr);
    exit(1);
  }

  spare_bytes = init_bytes - need_bytes;

  max_len = 0;
  for(i=0;i<ILEVELS;i++) {
    blocks_pointed[i] = pointers[i];
    for(j=0;j<(i-1);j++) {
      blocks_pointed[i] *= blockptrs;
    }
    if (j<i)
      blocks_pointed[i] *= blockptrs0;
    bytes_pointed[i] = blocks_pointed[i] * BLOCKSIZE;
    max_len += bytes_pointed[i];
  }

  printf("\n\n");
  printf("/*\n");
  printf(" * (generated by %s/%s)\n", NASD_CURD, __FILE__);
  printf(" *\n");
  printf(" * Node layout:\n");
  printf(" *  FFS-style n-level indirect.\n");
  printf(" *\n");
  printf(" * Direct pointer size:          %d\n", psize0);
  printf(" * Indirect pointer size:        %d\n", psize);
  printf(" * Space available for pointing: %" NASD_64s_FMT " (%" NASD_64s_FMT 
         " ipointers, %" NASD_64s_FMT " dpointers)\n",
    init_bytes, init_bytes/psize, init_bytes/psize0);
  printf(" * Spare space:                  %" NASD_64s_FMT " bytes (%" NASD_64s_FMT
         " ipointers, %" NASD_64s_FMT " dpointers)\n",
    spare_bytes, spare_bytes/psize, spare_bytes/psize0);
  for(i=0;i<ILEVELS;i++) {
    printf(" * Pointers at level %d:          %" NASD_64s_FMT "\n", i, pointers[i]);
  }
  for(i=0;i<ILEVELS;i++) {
    printf(" * Bytes pointed to at level %d: %" NASD_64s_FMT "\n", i, bytes_pointed[i]);
  }
  out_len("  2^32",         nasd_int64cast(0xffffffff));
  out_len("  2^33",        nasd_int64cast(0x1ffffffff));
  out_len("0.5 TB",       nasd_int64cast(0x7fffffffff));
  out_len("1.0 TB",       nasd_int64cast(0xffffffffff));
  out_len("3.0 TB",      nasd_int64cast(0x3ffffffffff));
  out_len("1.0 PB",    nasd_int64cast(0x3ffffffffffff));
  out_len("8.0 PB",   nasd_int64cast(0x1fffffffffffff));
  out_len("1.0 EB",  nasd_int64cast(0xfffffffffffffff));
  out_len("4.0 EB", nasd_int64cast(0x3fffffffffffffff));
  out_len("  2^63", nasd_int64cast(0x7fffffffffffffff));

  printf(" */\n");
  for(i=0;i<ILEVELS;i++) {
    printf("#define NASD_OD_LVL%d_PTRS    %" NASD_64s_FMT "\n", i, pointers[i]);
    printf("#define NASD_OD_LVL%d_POINTED nasd_int64cast(%" NASD_64s_FMT ")\n",
           i, blocks_pointed[i]);
  }
  printf("#define NASD_OD_ILVLS           %d\n", ILEVELS);
  printf("#define NASD_OD_NODE_PTRS0      %" NASD_64s_FMT "\n", node_pointers0);
  printf("#define NASD_OD_NODE_PTRS       %" NASD_64s_FMT "\n", node_pointers);
  printf("#define NASD_OD_MAX_OBJ_LEN     nasd_uint64cast(%" NASD_64s_FMT ")\n", max_len);
  printf("#define NASD_OD_PTR0_SIZE       %d\n", psize0);
  printf("#define NASD_OD_PTR_SIZE        %d\n", psize);
  printf("#define NASD_OD_PTR_BYTES       %" NASD_64s_FMT "\n", need_bytes);
  printf("#define NASD_OD_PTR_SPARE_BYTES %" NASD_64s_FMT "\n", spare_bytes);
  printf("/*                  2^64-1 = %" NASD_64u_FMT " (now you can stop asking :-) */\n",
    nasd_uint64cast(0xffffffffffffffff));;
  printf("#if NASD_OD_INCLUDE_COUNTS > 0\n");
  printf("static nasd_oblkno_t nasd_od_ilvl_top_ptrs[NASD_OD_ILVLS] = \n  {");
  for(i=0;i<ILEVELS;i++) {
    printf("%" NASD_64s_FMT, pointers[i]);
    if ((i+1) < ILEVELS)
      printf(", ");
  }
  printf("};\n");
  printf("static nasd_oblkno_t nasd_od_ilvl_ptrs[NASD_OD_ILVLS] = \n  {");
  for(i=0;i<ILEVELS;i++) {
    printf("nasd_int64cast(%" NASD_64s_FMT ")", blocks_pointed[i]);
    if ((i+1) < ILEVELS)
      printf(", ");
  }
  printf("};\n");
  sum = 0;
  printf("static nasd_oblkno_t nasd_od_ilvl_ptr_psum[NASD_OD_ILVLS] = \n  {");
  for(i=0;i<ILEVELS;i++) {
    printf("%" NASD_64s_FMT, sum);
    if ((i+1) < ILEVELS)
      printf(", ");
    if (i == 0)
      sum += pointers[i] * psize0;
    else
      sum += pointers[i] * psize;
  }
  printf("};\n");

  printf("static nasd_oblkno_t nasd_od_ilvl_leaves[NASD_OD_ILVLS] = \n  {");
  prod = 1;
  for(i=0;i<ILEVELS;i++) {
    printf("nasd_int64cast(%" NASD_64s_FMT ")", prod);
    if ((i+1) < ILEVELS)
      printf(", ");
    if (i == 0)
      prod *= blockptrs0;
    else
      prod *= blockptrs;
  }
  printf("};\n");

  printf("static nasd_offset_t nasd_od_ilvl_offset[NASD_OD_ILVLS] = {\n  ");
  offset = 0;
  for(i=0;i<ILEVELS;i++) {
    printf("nasd_int64cast(%" NASD_64u_FMT ")", offset);
    offset += bytes_pointed[i];
    if ((i+1) < ILEVELS)
      printf(",\n  ");
  }
  printf("};\n");

  printf("#endif /* NASD_OD_INCLUDE_COUNTS > 0 */\n");

  printf("\n");
  fflush(stdout);

  goto restart;
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
