view checksum/dosum.c @ 21:1c9dac05d040

Add lint-style FALLTHROUGH annotations to fallthrough cases. (in the parse engine and thus the output code) Document this, because the old output causes warnings with gcc10.
author David A. Holland
date Mon, 13 Jun 2022 00:04:38 -0400
parents 3aa0f5a02342
children
line wrap: on
line source

/*
 * AnaGram, A System for Syntax Directed Programming
 * Copyright 1993 Parsifal Software. All Rights Reserved.
 * Copyright 2006 David A. Holland. All Rights Reserved.
 * See the file COPYING for license and usage terms.
 *
 * dosum.c - checksums.
 */

/*
 * Fletcher's check-sum routines for AnaGram
 * 
 * Freely adapted from routines published in Dr. Dobbs Journal
 * May 1992, p. 64
 *
 * Revised as portable C in June 2006.
 */

#include <stdint.h>
#include <string.h>
#include <assert.h>

#include "bits.h"

struct sumstate {
  uint16_t k1, k2;
};

static void sumstate_init(struct sumstate *s, size_t length) {
  s->k1 = (uint16_t) (length >> 16);
  s->k2 = (uint16_t) length;

  if (s->k1 == 0) s->k1++;
  if (s->k2 == 0) s->k2++;
}

static void sumstate_sum(struct sumstate *s, const void *buf, size_t len) {
  const uint8_t *cb;
  unsigned i, n;
  uint16_t x;

  cb = buf;
  n = len/sizeof(uint16_t);
  for (i=0; i<n; i++) {

    // read one octet at a time so the byte order is deterministic
    x = *cb++;
    x += ((uint16_t)*cb++) << 8;

    s->k1 += x;
    if (s->k1 < x) s->k1++;
    s->k2 += s->k1;
    if (s->k2 < s->k1) s->k2++;
  }
}

static uint32_t sumstate_finish(struct sumstate *s) {
  uint32_t sum;

  s->k1 ^= 0xffff;
  s->k2 ^= 0xffff;
  sum = 65535UL*s->k2 + s->k1;

  return sum;
}

uint32_t dosum(const char *buf, size_t len, size_t skipstart, size_t skiplen) {
  struct sumstate s;
  size_t numwords;

  assert(skiplen%2==0);
  assert(skipstart==0 || skipstart+skiplen <= len);
  sumstate_init(&s, len);

  /*
   * It would be nice if we could count on skipstart being aligned.
   * But we can't. Nor is the filesize necessarily even.
   *
   * The followed mangled logic that rounds some things up matches
   * what used to be done in an even uglier way.
   *
   * To wit: if there's a skip area, we start after it and read pairs
   * of octets, including a zero past the end of the file if the
   * remaining data length after the skip area has odd size, so as to
   * get the last byte. (The caller is responsible for making sure
   * there's a zero there.) Then we read the beginning of the file up
   * to the skip area, but round down so as to not read the first byte
   * of the skip area if skipstart is odd. If skipstart is odd then we
   * wedge in that last byte along with a zero.
   *
   * If there isn't a skip area, we read the whole thing in order,
   * including the zero past the end if the size is odd.
   */

  assert(sizeof(uint16_t)==2);
  numwords = (len+1)/2;

  if (skipstart > 0) {
    size_t p0 = skipstart+skiplen;
    size_t l0 = (numwords - p0/2) * 2;
    size_t p1 = 0;
    size_t l1 = (skipstart/2) * 2;

    sumstate_sum(&s, buf+p0, l0);
    sumstate_sum(&s, buf+p1, l1);
    if (skipstart % 2 == 1) {
       char tmp[2];

       tmp[0] = buf[skipstart - 1];
       tmp[1] = 0;
       sumstate_sum(&s, tmp, 2);
    }
  }
  else {
    sumstate_sum(&s, buf, numwords * 2);
  }

  return sumstate_finish(&s);
}