Skip to Content.
Sympa Menu

freetds - [freetds] win64

freetds AT lists.ibiblio.org

Subject: FreeTDS Development Group

List archive

Chronological Thread  
  • From: "James K. Lowden" <jklowden AT freetds.org>
  • To: TDS Development Group <freetds AT lists.ibiblio.org>
  • Subject: [freetds] win64
  • Date: Sat, 17 Jan 2009 16:48:39 -0500

Sometimes work guides The Work. I built FreeTDS for 64-bit Windows this
week with VS 2005. I modified libtds to emit fewer warnings -- I left
some that are pretty egregious -- and added a .sln and .vcproj files to
the src directories.

As far as I know, I'm the first one to do this. There was barely any
support for it. For example, the winsock stuff was defined only for
WIN32. It's a big patch.

What follows are some notes on what I did in case it helps anyone else.

Win64 is an LLP64 architecture, whereas Linux uses LP64. The major
difference is that a Win64 long integer is still 32 bits while a Linux
long is 64 bits (cf.
http://www.ibm.com/developerworks/library/l-port64.html and
http://www.unix.org/version2/whatsnew/lp64_wp.html). size_t can hold the
maximum difference between two pointers. In Linux, size_t is the same as
an unsigned long. In Win64 there's no standard type equivalent to size_t.


There's an inherent tension in the C standard library between integers and
sizes. For example, strlen(3) returns size_t and sprintf(3) returns int.
To be meaninful, the values being compared have to fit within both
datatype's domains (which typically they do, but the compiler can't know
that and the handling of the atypical case will likely hurt). . 

To squelch the warnings, we add a cast. To be safe, that cast really
should be inside a conversion function that uses assert(3) to verify the
size_t values fits in 31 bits. I didn't do that, but it would be IMO a
worthwhile exercise.

Simlilarly, libtds plays fast and loose with int and unsigned int. Most
of the time it's really OK: how often will a column name length need more
than 2^31 bits? I find myself wondering if we shouldn't have a function
strleni() that returns an int after checking the size. .

Another way to handle this would be to make widespread use of a union,

typedef union tds_length {
unsigned long ulong;
size_t size;
int len;
} TDS_LENGTH;

and use checking functions (or macros)

size_t
as_size( TDS_LENGTH L )
{
assert(L.len > 0);
return L.size;
}

int
as_int( TDS_LENGTH L )
{
assert(L.size <= INT_MAX);
return L.len;
}

unsigned long
as_ulong( TDS_LENGTH L )
{
assert(L.len > 0);
return L.ulong;
}

to write such code as

TDS_LENGTH a;
a.len = sprintf(...);
if( as_size(a) != strlen(...) )
return FAIL;

That would constrain TDS_LENGTH to use values <= 2^31 is most cases. The
functions *assume* you're using the structure for type-punning. Short of
C++'s operator overloading, there's no easy way to check.

There are many places in libtds particularly where int is used where
size_t would have been more in keeping with the standard library. (I'm as
guilty as anyone else; old habits are hard to break.) Often, the type is
chosen to match a corresponding part of the TDS data strea, e.g.
TDSRESULTINFO::row_size is TDS_INT because that's how column widths are
defined in the TDS_ROWFMT2 packet. (But it's not used that way in
practice. tds_alloc_row() defines its local row_size as TDS_UINT,
accumulates the sizes of the columns, and assigns the sum to
TDSRESULTINFO::row_size. It would have been better to use size_t after
all.)

I think it would be better to use standard C types within the library and
as much as possible reserve casts to deal with TDS packets or API calls.
I wonder what others have done. It's not an easy question to research.

--jkl

Attachment: sizes.c
Description: Binary data




Archive powered by MHonArc 2.6.24.

Top of Page